From da88f8ea10fc2339e2126593a57c257dc4bc8bb1 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ghanem Date: Sun, 9 Oct 2022 12:05:17 +0200 Subject: [PATCH 1/8] update pynacl/python versions and bump version to 11.0b16 (#607) * bump version to 11.0b16... upgrade pyncal upgrade python remove requirements file and threesdk related files also remove example scripts (all related to bcdb) * pin pynacl to 1.4.0 as stellar-sdk depends... from poetry: stellar-sdk (2.10.0) depends on pynacl (>=1.3.0,<1.4.0) * pin pynacl to 1.3.0 as stellar-sdk depends... from poetry: stellar-sdk (2.10.0) depends on pynacl (>=1.3.0,<1.4.0) * upgrade pynacl to latest, older versions was for older stellar-sdk * update ci to drop ubuntu 18 and added 22/3.10 * fix 3.10 python version due to yaml parsing * drop python 3.6 and update more packages... update requests, jinja... --- .github/workflows/jsng-ci-macos.yml | 4 +- .github/workflows/jsng-ci.yml | 12 +- examplescripts/bcdb/test_bcdb.py | 109 ---- examplescripts/bcdb/test_index.db | Bin 40960 -> 0 bytes poetry.lock | 966 ++++++++++++++-------------- pyproject.toml | 13 +- requirements.txt | 375 ----------- threesdk.spec | 36 -- 8 files changed, 494 insertions(+), 1021 deletions(-) delete mode 100644 examplescripts/bcdb/test_bcdb.py delete mode 100644 examplescripts/bcdb/test_index.db delete mode 100644 requirements.txt delete mode 100644 threesdk.spec diff --git a/.github/workflows/jsng-ci-macos.yml b/.github/workflows/jsng-ci-macos.yml index c3a51ae71..b1f1e3910 100644 --- a/.github/workflows/jsng-ci-macos.yml +++ b/.github/workflows/jsng-ci-macos.yml @@ -2,7 +2,7 @@ name: jsng-ci_macos -on: +on: workflow_dispatch: jobs: @@ -14,7 +14,7 @@ jobs: # max-parallel: 9 matrix: os: [macos-10.15] - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: [3.7, 3.8, 3.9, '3.10'] experimental: [true] steps: - uses: actions/checkout@master diff --git a/.github/workflows/jsng-ci.yml b/.github/workflows/jsng-ci.yml index 877bebaa6..ef7358ded 100644 --- a/.github/workflows/jsng-ci.yml +++ b/.github/workflows/jsng-ci.yml @@ -17,16 +17,12 @@ jobs: fail-fast: false # max-parallel: 9 matrix: - os: [ubuntu-18.04] - python-version: [3.6, 3.7, 3.8] + os: [ubuntu-20.04] + python-version: [3.7, 3.8, 3.9] experimental: [false] include: - - os: ubuntu-20.04 - python-version: 3.8 - experimental: true - name: Experimental build - latest Ubuntu - - os: ubuntu-20.04 - python-version: 3.9 + - os: ubuntu-22.04 + python-version: '3.10' experimental: true name: Experimental build - latest Python steps: diff --git a/examplescripts/bcdb/test_bcdb.py b/examplescripts/bcdb/test_bcdb.py deleted file mode 100644 index db913f03a..000000000 --- a/examplescripts/bcdb/test_bcdb.py +++ /dev/null @@ -1,109 +0,0 @@ -from jumpscale.loader import j -from pprint import pprint -import os - -# FIXME: re-write this test and add the ability to flush/reset a bcdb instance - -with open("test_index.db", "w"): - pass - -# print(os.system("redis-cli --scan --pattern 'test.indexer.quote.id://*' | xargs redis-cli del")) -# print(os.system("redis-cli --scan --pattern 'test.quote://*' | xargs redis-cli del")) -# print(os.system("redis-cli --scan --pattern 'test.quote.las*' | xargs redis-cli del")) -# print(os.system("redis-cli --scan --pattern 'test.indexer.employee.id://*' | xargs redis-cli del")) -# print(os.system("redis-cli --scan --pattern 'test.employee://*' | xargs redis-cli del")) -# print(os.system("redis-cli --scan --pattern 'test.employee.las*' | xargs redis-cli del")) -# print(os.system("redis-cli --scan --pattern 'test.indexer.db.id://*' | xargs redis-cli del")) -# print(os.system("redis-cli --scan --pattern 'test.db://*' | xargs redis-cli del")) -# print(os.system("redis-cli --scan --pattern 'test.db.las*' | xargs redis-cli del")) -j.core.db.flushall() -try: - j.clients.sonic.get("test").flush_collection("test") -except: - pass - -bcdb = j.data.bcdb.BCDB("test") -emp_model = bcdb.get_model_by_name("employee") -omar = emp_model.create_obj({"name": "omar", "age": 21, "salary": 1500}) -ahmed = emp_model.create_obj({"name": "ahmed", "age": 30, "salary": 1300}) -emp_model.save_obj(omar) -emp_model.save_obj(ahmed) -print("------------------------------------------employee-----------------------------------------------------------") -print("From 1000 to 1400") -pprint([emp_model.get_dict(x) for x in emp_model.get_range("salary", 1000, 1400)]) -print("From 1000 to 1600") -pprint([emp_model.get_dict(x) for x in emp_model.get_range("salary", 1000, 1600)]) -print("From 1300 to 1400") -pprint([x.get_dict() for x in emp_model.get_range("salary", 1300, 1400)]) -print("------------------------------------------quotes---------------------------------------------------------------") -quote_model = bcdb.get_model_by_name("quote") -quote0 = quote_model.create_obj( - { - "author": "John Keats", - "quote": "I love you the more in that I believe you had liked me for my own sake and for nothing else.", - } -) -quote1 = quote_model.create_obj({"author": "Indira Gandhi", "quote": "You cannot shake hands with a clenched fist."}) -quote2 = quote_model.create_obj( - { - "author": "Amelia Earhart", - "quote": "The most difficult thing is the decision to act, the rest is merely tenacity. The fears are paper tigers. You can do anything you decide to do. You can act to change and control your life; and the procedure, the process is its own reward.", - } -) -quote3 = quote_model.create_obj({"author": "Leonardo da Vinci", "quote": "Learning never exhausts the mind."}) -quote4 = quote_model.create_obj( - { - "author": "Francis of Assisi", - "quote": "Lord, make me an instrument of thy peace. Where there is hatred, let me sow love.", - } -) -quote5 = quote_model.create_obj( - {"author": "Walt Whitman", "quote": "Keep your face always toward the sunshine - and shadows will fall behind you."} -) -quote6 = quote_model.create_obj({"author": "George Orwell", "quote": "Happiness can exist only in acceptance."}) -quote7 = quote_model.create_obj( - {"author": "Thomas Jefferson", "quote": "Honesty is the first chapter in the book of wisdom."} -) -quote8 = quote_model.create_obj({"author": "Anne Frank", "quote": "Whoever is happy will make others happy too."}) -quote9 = quote_model.create_obj( - {"author": "John C. Maxwell", "quote": "A leader is one who knows the way, goes the way, and shows the way."} -) -quote10 = quote_model.create_obj({"author": "Milton Berle", "quote": "If opportunity doesn't knock, build a door."}) -quote11 = quote_model.create_obj( - { - "author": "Plato", - "quote": "Wise men speak because they have something to say; Fools because they have to say something.", - } -) -quote12 = quote_model.create_obj( - {"author": "Leo Buscaglia", "quote": "A single rose can be my garden... a single friend, my world."} -) -quote13 = quote_model.create_obj( - {"author": "Soren Kierkegaard", "quote": "Life is not a problem to be solved, but a reality to be experienced."} -) -quote_model.save_obj(quote0) -quote_model.save_obj(quote1) -quote_model.save_obj(quote2) -quote_model.save_obj(quote3) -quote_model.save_obj(quote4) -quote_model.save_obj(quote5) -quote_model.save_obj(quote6) -quote_model.save_obj(quote7) -quote_model.save_obj(quote8) -quote_model.save_obj(quote9) -quote_model.save_obj(quote10) -quote_model.save_obj(quote11) -quote_model.save_obj(quote12) -quote_model.save_obj(quote13) - -pprint("Quotes that contains the word 'Life'") -pprint([x.get_dict() for x in quote_model.get_pattern("quote", "decision")]) - -pprint("--------------------------------dbs---------------------------------------------") - -db_model = bcdb.get_model_by_name("db") - -redis = db_model.create_obj({"host": "localhost", "user": {"username": "omar", "password": "omar password"}}) -db_model.save_obj(redis) -retrieved = db_model.get_by("host", "localhost") -pprint(retrieved.get_dict()) diff --git a/examplescripts/bcdb/test_index.db b/examplescripts/bcdb/test_index.db deleted file mode 100644 index c170246cacd4f517c74d56b48dccb8322d5b0613..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40960 zcmeI*&u`mg7zc2_eq$$2V?WK-$l_4(R%lv7x0PxjIDkZ3vZ`G-*c#a25;^tT#%f}( zPO7aELgUJbKY$A-Zd^bhapD9Lf(s`kCd2`8*kJ;;A%u3|eI24UYkTabRbNYf?7V*S z{`gcmIPc?h<&JQ~EjWaJ5 zT>8gw+?xii(zjIbKJlLO-gSR=Z@OO`^fr`300Izz00bZa0SG_<0uVTQ0?!w0zg%W- z_JU?xhRv|)%wMj})@!0ZJGWdDVN*;-p@`z1xRyqpAl()la=ZGBWBV_bnGwaIywTlk zt6R86u+>Yl*Nw2*mYWU36Hbw zY~-?Yt(|O3+2{ssCDNbHpwJqc(V%pws-D!~!eqaV>f7A*&yKUr;SS=UBZr&M?ce-P z#i7P^aka_v{`fe1-_Ohj9S@G?kldhje>~k>Mt4f|hbHaL^+_t|Pm`;5lYTeoo(k9?009U<00Izz00bZa0SG_<0uXqD1xjX)`KB4PIx@7z ziY(_;%tU>gWZlvW{3iWv&;u2)K>z{}fB*y_009U<00Izz00ba#gajkNTHJK3Tl)Eb&%1BXpY#iTN1xG0^Z{L` zfG*L?^ejD1C3O)S1Rwwb2tWV=5P$##AOHafK;TIfsCb-Bdn-vQV{tx`>4sbnf;23< zoSk%+Wg_Obx~su@I|`fkM>sn%av^H>)XxXb$+Rtx7de|ME+ws4%%2e# zf*aRmyX`rgO*pf0EXBDrh&RRxoSiPzTWYbrE0*NinoPS%T+XYm@(VIa*QIzp)o&cN zRr~gppxqN!T2ZeP#1)mMKW*t&R->*+*2HYL8+D^fP8H|WFk?05M1+BOD^fL%an7oI zwj3qc&VBA0PzA{+C08T7h({okWcv*-Wm54ugisqX=P zq95sd`i8!yujq4i5gP;`009U<00Izz00bZa0SG_<0*_EYKez9BI=ZforIL;#BRUp~ zIy#Pyg@TUxypFc5qh;xs%jw9ujyuo)d&CX;f!?8y={@?D&e31=6n#lA&=hfs=)*@i zL39NH2tWV=5P$##AOHafKmY;|I4pq*pJsb<0lYFl$%aw^T>bETmI+WA=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] +docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] name = "backcall" @@ -65,15 +57,12 @@ python-versions = "*" [[package]] name = "bcrypt" -version = "3.2.2" +version = "4.0.0" description = "Modern password hashing for your software and your servers" category = "main" optional = false python-versions = ">=3.6" -[package.dependencies] -cffi = ">=1.1" - [package.extras] tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] @@ -116,7 +105,7 @@ python-versions = "*" [[package]] name = "certifi" -version = "2022.6.15" +version = "2022.9.24" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -135,22 +124,26 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "2.0.12" +version = "2.1.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false -python-versions = ">=3.5.0" +python-versions = ">=3.6.0" [package.extras] unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "7.1.2" +version = "8.1.3" description = "Composable command line interface toolkit" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "codecov" @@ -174,18 +167,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" -version = "6.2" +version = "6.5.0" description = "Code coverage measurement for Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] toml = ["tomli"] [[package]] name = "cryptography" -version = "37.0.4" +version = "38.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -196,11 +189,11 @@ cffi = ">=1.12" [package.extras] docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] -docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] -sdist = ["setuptools_rust (>=0.11.4)"] +sdist = ["setuptools-rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] [[package]] name = "decorator" @@ -212,11 +205,11 @@ python-versions = ">=3.5" [[package]] name = "dill" -version = "0.3.4" +version = "0.3.5.1" description = "serialize all of python" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" [package.extras] graph = ["objgraph (>=1.7.2)"] @@ -245,7 +238,7 @@ websocket-client = ">=0.32.0" [package.extras] ssh = ["paramiko (>=2.4.2)"] -tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] +tls = ["cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=17.5.0)"] [[package]] name = "docopt" @@ -273,7 +266,7 @@ pytest = ["mock (>=2.0.0,<3.0)", "pytest (>=3.2.5,<4.0)"] testing = ["mock (>=2.0.0,<3.0)"] [[package]] -name = "faker" +name = "Faker" version = "2.0.5" description = "Faker is a Python package that generates fake data for you." category = "main" @@ -310,6 +303,7 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5" [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} greenlet = {version = ">=1.1.0,<2.0", markers = "platform_python_implementation == \"CPython\""} +setuptools = "*" "zope.event" = "*" "zope.interface" = "*" @@ -317,8 +311,8 @@ greenlet = {version = ">=1.1.0,<2.0", markers = "platform_python_implementation dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] docs = ["repoze.sphinx.autointerface", "sphinxcontrib-programoutput", "zope.schema"] monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "selectors2", "backports.socketpair", "psutil (>=5.7.0)"] -test = ["requests", "objgraph", "cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "selectors2", "futures", "mock", "backports.socketpair", "contextvars (==2.4)", "coverage (>=5.0)", "coveralls (>=1.7.0)", "psutil (>=5.7.0)"] +recommended = ["backports.socketpair", "cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)", "selectors2"] +test = ["backports.socketpair", "cffi (>=1.12.2)", "contextvars (==2.4)", "coverage (>=5.0)", "coveralls (>=1.7.0)", "dnspython (>=1.16.0,<2.0)", "futures", "idna", "mock", "objgraph", "psutil (>=5.7.0)", "requests", "selectors2"] [[package]] name = "gitdb" @@ -332,40 +326,40 @@ python-versions = ">=3.6" smmap = ">=3.0.1,<6" [[package]] -name = "gitpython" -version = "3.1.20" -description = "Python Git Library" +name = "GitPython" +version = "3.1.28" +description = "GitPython is a python library used to interact with Git repositories" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} [[package]] name = "graphviz" -version = "0.19.1" +version = "0.20.1" description = "Simple Python interface for Graphviz" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -dev = ["tox (>=3)", "flake8", "pep8-naming", "wheel", "twine"] -docs = ["sphinx (>=1.8)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] -test = ["pytest (>=6)", "pytest-mock (>=3)", "mock (>=4)", "pytest-cov", "coverage"] +dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] +docs = ["sphinx (>=5)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +test = ["coverage", "mock (>=4)", "pytest (>=7)", "pytest-cov", "pytest-mock (>=3)"] [[package]] name = "greenlet" -version = "1.1.2" +version = "1.1.3" description = "Lightweight in-process concurrent programming" category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" [package.extras] -docs = ["sphinx"] +docs = ["Sphinx"] [[package]] name = "hypothesis" @@ -382,7 +376,7 @@ sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] all = ["django (>=1.11)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "numpy (>=1.9.0)", "pandas (>=0.19)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)"] dateutil = ["python-dateutil (>=1.4)"] -django = ["pytz (>=2014.1)", "django (>=1.11)"] +django = ["django (>=1.11)", "pytz (>=2014.1)"] dpcontracts = ["dpcontracts (>=0.4)"] lark = ["lark-parser (>=0.6.5)"] numpy = ["numpy (>=1.9.0)"] @@ -392,7 +386,7 @@ pytz = ["pytz (>=2014.1)"] [[package]] name = "idna" -version = "3.3" +version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false @@ -400,24 +394,32 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.8.3" +version = "5.0.0" description = "Read metadata from Python packages" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" [[package]] name = "invoke" -version = "1.7.1" +version = "1.7.3" description = "Pythonic task execution" category = "main" optional = false @@ -433,45 +435,40 @@ python-versions = ">=2.7" [package.dependencies] ipython = {version = ">=5.1.0", markers = "python_version >= \"3.4\""} +setuptools = "*" [[package]] name = "ipython" -version = "7.16.3" +version = "7.34.0" description = "IPython: Productive Interactive Computing" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] appnope = {version = "*", markers = "sys_platform == \"darwin\""} backcall = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" -jedi = ">=0.10,<=0.17.2" -pexpect = {version = "*", markers = "sys_platform != \"win32\""} +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} pickleshare = "*" prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" pygments = "*" +setuptools = ">=18.5" traitlets = ">=4.2" [package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] doc = ["Sphinx (>=1.3)"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] -notebook = ["notebook", "ipywidgets"] +notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] - -[[package]] -name = "ipython-genutils" -version = "0.2.0" -description = "Vestigial utilities from IPython" -category = "dev" -optional = false -python-versions = "*" +test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"] [[package]] name = "jedi" @@ -489,18 +486,18 @@ qa = ["flake8 (==3.7.9)"] testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] -name = "jinja2" -version = "2.11.3" +name = "Jinja2" +version = "3.1.2" description = "A very fast and expressive template engine." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" [package.dependencies] -MarkupSafe = ">=0.23" +MarkupSafe = ">=2.0" [package.extras] -i18n = ["Babel (>=0.8)"] +i18n = ["Babel (>=2.7)"] [[package]] name = "libtmux" @@ -523,30 +520,32 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["black (>=19.3b0)", "sphinx-rtd-theme (>=0.3)", "sphinx-autobuild (>=0.7)", "Sphinx (>=1.7.4)", "pytest-cov (>=2.7.1)", "pytest (>=4.6.2)", "tox-travis (>=0.12)", "tox (>=3.9.0)", "isort (>=4.3.20)", "flake8 (>=3.7.7)", "colorama (>=0.3.4)", "codecov (>=2.0.15)"] +dev = ["Sphinx (>=1.7.4)", "black (>=19.3b0)", "codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=4.3.20)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7)", "sphinx-rtd-theme (>=0.3)", "tox (>=3.9.0)", "tox-travis (>=0.12)"] [[package]] -name = "mako" -version = "1.1.6" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +name = "Mako" +version = "1.2.3" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.7" [package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} MarkupSafe = ">=0.9.2" [package.extras] -babel = ["babel"] +babel = ["Babel"] lingua = ["lingua"] +testing = ["pytest"] [[package]] -name = "markdown" -version = "3.3.7" +name = "Markdown" +version = "3.4.1" description = "Python implementation of Markdown." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} @@ -555,28 +554,31 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} testing = ["coverage", "pyyaml"] [[package]] -name = "markupsafe" -version = "2.0.1" +name = "MarkupSafe" +version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" + +[package.dependencies] +traitlets = "*" [[package]] -name = "more-itertools" -version = "8.13.0" -description = "More routines for operating on iterables, beyond itertools" +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = "*" [[package]] name = "msgpack" @@ -634,9 +636,9 @@ pynacl = ">=1.0.1" six = "*" [package.extras] -all = ["pyasn1 (>=0.1.7)", "pynacl (>=1.0.1)", "bcrypt (>=3.1.3)", "invoke (>=1.3)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] -ed25519 = ["pynacl (>=1.0.1)", "bcrypt (>=3.1.3)"] -gssapi = ["pyasn1 (>=0.1.7)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] +all = ["bcrypt (>=3.1.3)", "gssapi (>=1.4.1)", "invoke (>=1.3)", "pyasn1 (>=0.1.7)", "pynacl (>=1.0.1)", "pywin32 (>=2.1.8)"] +ed25519 = ["bcrypt (>=3.1.3)", "pynacl (>=1.0.1)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] invoke = ["invoke (>=1.3)"] [[package]] @@ -648,7 +650,7 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -testing = ["pytest (>=3.0.7)", "docopt"] +testing = ["docopt", "pytest (>=3.0.7)"] [[package]] name = "pathlib2" @@ -702,17 +704,18 @@ python-versions = "*" [[package]] name = "pluggy" -version = "0.13.1" +version = "1.0.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" @@ -720,7 +723,7 @@ version = "2.0.10" description = "Library for building powerful interactive command lines in Python" category = "main" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=2.6,<3.0.0 || >=3.3.0" [package.dependencies] six = ">=1.9.0" @@ -728,14 +731,14 @@ wcwidth = "*" [[package]] name = "psutil" -version = "5.9.1" +version = "5.9.2" description = "Cross-platform lib for process and system monitoring in Python." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] [[package]] name = "ptpython" @@ -807,13 +810,16 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -name = "pygments" -version = "2.12.0" +name = "Pygments" +version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false python-versions = ">=3.6" +[package.extras] +plugins = ["importlib-metadata"] + [[package]] name = "pylzma" version = "0.5.0" @@ -823,54 +829,51 @@ optional = false python-versions = "*" [[package]] -name = "pynacl" -version = "1.3.0" +name = "PyNaCl" +version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" [package.dependencies] cffi = ">=1.4.1" -six = "*" [package.extras] -docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] +docs = ["sphinx (>=1.6.5)", "sphinx_rtd_theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] [[package]] name = "pyparsing" -version = "3.0.7" -description = "Python parsing module" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.8" [package.extras] diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "5.4.3" +version = "7.1.3" description = "pytest: simple powerful testing with Python" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=17.4.0" +attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -more-itertools = ">=4.0.0" +iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0" -py = ">=1.5.0" -wcwidth = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" [package.extras] -checkqa-mypy = ["mypy (==v0.761)"] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] [[package]] name = "pytest-cov" @@ -886,7 +889,7 @@ pytest = ">=4.6" toml = "*" [package.extras] -testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "python-dateutil" @@ -916,7 +919,7 @@ optional = false python-versions = "*" [[package]] -name = "pyyaml" +name = "PyYAML" version = "5.4.1" description = "YAML parser and emitter for Python" category = "main" @@ -936,21 +939,21 @@ hiredis = ["hiredis (>=0.1.3)"] [[package]] name = "requests" -version = "2.27.1" +version = "2.28.1" description = "Python HTTP for Humans." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "secretconf" @@ -960,6 +963,19 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "setuptools" +version = "65.4.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -1009,40 +1025,43 @@ optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] -name = "traitlets" -version = "4.3.3" -description = "Traitlets Python config system" +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" -[package.dependencies] -decorator = "*" -ipython-genutils = "*" -six = "*" +[[package]] +name = "traitlets" +version = "5.4.0" +description = "" +category = "dev" +optional = false +python-versions = ">=3.7" [package.extras] -test = ["pytest", "mock"] +test = ["pre-commit", "pytest"] [[package]] name = "typing-extensions" -version = "4.1.1" -description = "Backported and Experimental Type Hints for Python 3.6+" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "urllib3" -version = "1.26.11" +version = "1.26.12" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" [package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -1076,19 +1095,19 @@ python-versions = "*" [[package]] name = "websocket-client" -version = "1.3.1" +version = "1.4.1" description = "WebSocket client for Python with low level API options" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] test = ["websockets"] -optional = ["wsaccel", "python-socks"] -docs = ["sphinx-rtd-theme (>=0.5)", "Sphinx (>=3.4)"] [[package]] -name = "whoosh" +name = "Whoosh" version = "2.7.4" description = "Fast, pure-Python full text indexing, search, and spell checking library." category = "main" @@ -1108,15 +1127,15 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] [[package]] name = "zipp" -version = "3.6.0" +version = "3.9.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "zope.event" @@ -1126,8 +1145,11 @@ category = "main" optional = false python-versions = "*" +[package.dependencies] +setuptools = "*" + [package.extras] -docs = ["sphinx"] +docs = ["Sphinx"] test = ["zope.testrunner"] [[package]] @@ -1138,15 +1160,18 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[package.dependencies] +setuptools = "*" + [package.extras] -docs = ["sphinx", "repoze.sphinx.autointerface"] +docs = ["Sphinx", "repoze.sphinx.autointerface"] test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" -python-versions = "^3.6" -content-hash = "6ec369a5e17640d7233b838494be06bf45981e5fb48e2287bf8cc6e6c9cee2ee" +python-versions = ">=3.7,<4.0" +content-hash = "56d9425d3c1b93c2df9697511d59a8a1471a7fea0b28e917bb00f0e934a004ce" [metadata.files] appdirs = [ @@ -1165,9 +1190,6 @@ arrow = [ {file = "arrow-0.15.8-py2.py3-none-any.whl", hash = "sha256:271b8e05174d48e50324ed0dc5d74796c839c7e579a4f21cf1a7394665f9e94f"}, {file = "arrow-0.15.8.tar.gz", hash = "sha256:edc31dc051db12c95da9bac0271cd1027b8e36912daf6d4580af53b23e62721a"}, ] -atomicwrites = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, -] attrs = [ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, @@ -1177,17 +1199,18 @@ backcall = [ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] bcrypt = [ - {file = "bcrypt-3.2.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:7180d98a96f00b1050e93f5b0f556e658605dd9f524d0b0e68ae7944673f525e"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:61bae49580dce88095d669226d5076d0b9d927754cedbdf76c6c9f5099ad6f26"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88273d806ab3a50d06bc6a2fc7c87d737dd669b76ad955f449c43095389bc8fb"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6d2cb9d969bfca5bc08e45864137276e4c3d3d7de2b162171def3d188bf9d34a"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b02d6bfc6336d1094276f3f588aa1225a598e27f8e3388f4db9948cb707b521"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c46100e315c3a5b90fdc53e429c006c5f962529bc27e1dfd656292c20ccc40"}, - {file = "bcrypt-3.2.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7d9ba2e41e330d2af4af6b1b6ec9e6128e91343d0b4afb9282e54e5508f31baa"}, - {file = "bcrypt-3.2.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cd43303d6b8a165c29ec6756afd169faba9396a9472cdff753fe9f19b96ce2fa"}, - {file = "bcrypt-3.2.2-cp36-abi3-win32.whl", hash = "sha256:4e029cef560967fb0cf4a802bcf4d562d3d6b4b1bf81de5ec1abbe0f1adb027e"}, - {file = "bcrypt-3.2.2-cp36-abi3-win_amd64.whl", hash = "sha256:7ff2069240c6bbe49109fe84ca80508773a904f5a8cb960e02a977f7f519b129"}, - {file = "bcrypt-3.2.2.tar.gz", hash = "sha256:433c410c2177057705da2a9f2cd01dd157493b2a7ac14c8593a16b3dab6b6bfb"}, + {file = "bcrypt-4.0.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:845b1daf4df2dd94d2fdbc9454953ca9dd0e12970a0bfc9f3dcc6faea3fa96e4"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8780e69f9deec9d60f947b169507d2c9816e4f11548f1f7ebee2af38b9b22ae4"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c3334446fac200499e8bc04a530ce3cf0b3d7151e0e4ac5c0dddd3d95e97843"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb67f6a6c72dfb0a02f3df51550aa1862708e55128b22543e2b42c74f3620d7"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:7c7dd6c1f05bf89e65261d97ac3a6520f34c2acb369afb57e3ea4449be6ff8fd"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:594780b364fb45f2634c46ec8d3e61c1c0f1811c4f2da60e8eb15594ecbf93ed"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d0dd19aad87e4ab882ef1d12df505f4c52b28b69666ce83c528f42c07379227"}, + {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bf413f2a9b0a2950fc750998899013f2e718d20fa4a58b85ca50b6df5ed1bbf9"}, + {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ede0f506554571c8eda80db22b83c139303ec6b595b8f60c4c8157bdd0bdee36"}, + {file = "bcrypt-4.0.0-cp36-abi3-win32.whl", hash = "sha256:dc6ec3dc19b1c193b2f7cf279d3e32e7caf447532fbcb7af0906fe4398900c33"}, + {file = "bcrypt-4.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:0b0f0c7141622a31e9734b7f649451147c04ebb5122327ac0bd23744df84be90"}, + {file = "bcrypt-4.0.0.tar.gz", hash = "sha256:c59c170fc9225faad04dde1ba61d85b413946e8ce2e5f5f5ff30dfd67283f319"}, ] better-exceptions = [ {file = "better_exceptions-0.2.3-py3-none-any.whl", hash = "sha256:28a851a58155cac9e677d632fba64b101151e4c6b3a5c92ecd27ca17e9bbd005"}, @@ -1202,8 +1225,8 @@ bottle = [ {file = "bottle-0.12.23.tar.gz", hash = "sha256:683de3aa399fb26e87b274dbcf70b1a651385d459131716387abdc3792e04167"}, ] certifi = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, + {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, + {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, ] cffi = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, @@ -1272,16 +1295,15 @@ cffi = [ {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, - {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, ] click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] codecov = [ {file = "codecov-2.1.12-py2.py3-none-any.whl", hash = "sha256:585dc217dc3d8185198ceb402f85d5cb5dbfa0c5f350a5abcdf9e347776a5b47"}, - {file = "codecov-2.1.12-py3.8.egg", hash = "sha256:782a8e5352f22593cbc5427a35320b99490eb24d9dcfa2155fd99d2b75cfb635"}, {file = "codecov-2.1.12.tar.gz", hash = "sha256:a0da46bb5025426da895af90938def8ee12d37fcbcbbbc15b6dc64cf7ebc51c1"}, ] colorama = [ @@ -1289,85 +1311,92 @@ colorama = [ {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] coverage = [ - {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, - {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, - {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, - {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, - {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, - {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, - {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, - {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, - {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, - {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, - {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, - {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, - {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, - {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, - {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, - {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, - {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, - {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, - {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, - {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] cryptography = [ - {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, - {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"}, - {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"}, - {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"}, - {file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"}, - {file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"}, - {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"}, - {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, - {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, + {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f"}, + {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6"}, + {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a"}, + {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294"}, + {file = "cryptography-38.0.1-cp36-abi3-win32.whl", hash = "sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0"}, + {file = "cryptography-38.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a"}, + {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d"}, + {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9"}, + {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b"}, + {file = "cryptography-38.0.1.tar.gz", hash = "sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7"}, ] decorator = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] dill = [ - {file = "dill-0.3.4-py2.py3-none-any.whl", hash = "sha256:7e40e4a70304fd9ceab3535d36e58791d9c4a776b38ec7f7ec9afc8d3dca4d4f"}, - {file = "dill-0.3.4.zip", hash = "sha256:9f9734205146b2b353ab3fec9af0070237b6ddae78452af83d2fca84d739e675"}, + {file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"}, + {file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"}, ] distro = [ {file = "distro-1.7.0-py3-none-any.whl", hash = "sha256:d596311d707e692c2160c37807f83e3820c5d539d5a83e87cfb6babd8ba3a06b"}, @@ -1384,7 +1413,7 @@ fabric = [ {file = "fabric-2.7.1-py2.py3-none-any.whl", hash = "sha256:7610362318ef2d391cc65d4befb684393975d889ed5720f23499394ec0e136fa"}, {file = "fabric-2.7.1.tar.gz", hash = "sha256:76f8fef59cf2061dbd849bbce4fe49bdd820884385004b0ca59136ac3db129e4"}, ] -faker = [ +Faker = [ {file = "Faker-2.0.5-py2.py3-none-any.whl", hash = "sha256:c1d19f8c63487b4711189403f5c2e06af1dd8b5be7928f6547f5f7cddd13d917"}, {file = "Faker-2.0.5.tar.gz", hash = "sha256:29093e61f12745150774fd05ab499e87252a4fa51ed51e45c40179e854f87925"}, ] @@ -1431,105 +1460,104 @@ gitdb = [ {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] -gitpython = [ - {file = "GitPython-3.1.20-py3-none-any.whl", hash = "sha256:b1e1c269deab1b08ce65403cf14e10d2ef1f6c89e33ea7c5e5bb0222ea593b8a"}, - {file = "GitPython-3.1.20.tar.gz", hash = "sha256:df0e072a200703a65387b0cfdf0466e3bab729c0458cf6b7349d0e9877636519"}, +GitPython = [ + {file = "GitPython-3.1.28-py3-none-any.whl", hash = "sha256:77bfbd299d8709f6af7e0c70840ef26e7aff7cf0c1ed53b42dd7fc3a310fcb02"}, + {file = "GitPython-3.1.28.tar.gz", hash = "sha256:6bd3451b8271132f099ceeaf581392eaf6c274af74bb06144307870479d0697c"}, ] graphviz = [ - {file = "graphviz-0.19.1-py3-none-any.whl", hash = "sha256:f34088c08be2ec16279dfa9c3b4ff3d1453c5c67597a33e2819b000e18d4c546"}, - {file = "graphviz-0.19.1.zip", hash = "sha256:09ed0cde452d015fe77c4845a210eb642f28d245f5bc250d4b97808cb8f49078"}, + {file = "graphviz-0.20.1-py3-none-any.whl", hash = "sha256:587c58a223b51611c0cf461132da386edd896a029524ca61a1462b880bf97977"}, + {file = "graphviz-0.20.1.zip", hash = "sha256:8c58f14adaa3b947daf26c19bc1e98c4e0702cdc31cf99153e6f06904d492bf8"}, ] greenlet = [ - {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d"}, - {file = "greenlet-1.1.2-cp27-cp27m-win32.whl", hash = "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713"}, - {file = "greenlet-1.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8"}, - {file = "greenlet-1.1.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, - {file = "greenlet-1.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965"}, - {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, - {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c"}, - {file = "greenlet-1.1.2-cp35-cp35m-win32.whl", hash = "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963"}, - {file = "greenlet-1.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e"}, - {file = "greenlet-1.1.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, - {file = "greenlet-1.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f"}, - {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, - {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, - {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, - {file = "greenlet-1.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe"}, - {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, - {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, - {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, - {file = "greenlet-1.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2"}, - {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, - {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, - {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, - {file = "greenlet-1.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3"}, - {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, - {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, - {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, + {file = "greenlet-1.1.3-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b"}, + {file = "greenlet-1.1.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32"}, + {file = "greenlet-1.1.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7c5227963409551ae4a6938beb70d56bf1918c554a287d3da6853526212fbe0a"}, + {file = "greenlet-1.1.3-cp27-cp27m-win32.whl", hash = "sha256:9fae214f6c43cd47f7bef98c56919b9222481e833be2915f6857a1e9e8a15318"}, + {file = "greenlet-1.1.3-cp27-cp27m-win_amd64.whl", hash = "sha256:de431765bd5fe62119e0bc6bc6e7b17ac53017ae1782acf88fcf6b7eae475a49"}, + {file = "greenlet-1.1.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:510c3b15587afce9800198b4b142202b323bf4b4b5f9d6c79cb9a35e5e3c30d2"}, + {file = "greenlet-1.1.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:9951dcbd37850da32b2cb6e391f621c1ee456191c6ae5528af4a34afe357c30e"}, + {file = "greenlet-1.1.3-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:07c58e169bbe1e87b8bbf15a5c1b779a7616df9fd3e61cadc9d691740015b4f8"}, + {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df02fdec0c533301497acb0bc0f27f479a3a63dcdc3a099ae33a902857f07477"}, + {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c88e134d51d5e82315a7c32b914a58751b7353eb5268dbd02eabf020b4c4700"}, + {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b41d19c0cfe5c259fe6c539fd75051cd39a5d33d05482f885faf43f7f5e7d26"}, + {file = "greenlet-1.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:6f5d4b2280ceea76c55c893827961ed0a6eadd5a584a7c4e6e6dd7bc10dfdd96"}, + {file = "greenlet-1.1.3-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:184416e481295832350a4bf731ba619a92f5689bf5d0fa4341e98b98b1265bd7"}, + {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd0404d154084a371e6d2bafc787201612a1359c2dee688ae334f9118aa0bf47"}, + {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a43bbfa9b6cfdfaeefbd91038dde65ea2c421dc387ed171613df340650874f2"}, + {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce5b64dfe8d0cca407d88b0ee619d80d4215a2612c1af8c98a92180e7109f4b5"}, + {file = "greenlet-1.1.3-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:903fa5716b8fbb21019268b44f73f3748c41d1a30d71b4a49c84b642c2fed5fa"}, + {file = "greenlet-1.1.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0118817c9341ef2b0f75f5af79ac377e4da6ff637e5ee4ac91802c0e379dadb4"}, + {file = "greenlet-1.1.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:466ce0928e33421ee84ae04c4ac6f253a3a3e6b8d600a79bd43fd4403e0a7a76"}, + {file = "greenlet-1.1.3-cp35-cp35m-win32.whl", hash = "sha256:65ad1a7a463a2a6f863661329a944a5802c7129f7ad33583dcc11069c17e622c"}, + {file = "greenlet-1.1.3-cp35-cp35m-win_amd64.whl", hash = "sha256:7532a46505470be30cbf1dbadb20379fb481244f1ca54207d7df3bf0bbab6a20"}, + {file = "greenlet-1.1.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:caff52cb5cd7626872d9696aee5b794abe172804beb7db52eed1fd5824b63910"}, + {file = "greenlet-1.1.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:db41f3845eb579b544c962864cce2c2a0257fe30f0f1e18e51b1e8cbb4e0ac6d"}, + {file = "greenlet-1.1.3-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8533f5111704d75de3139bf0b8136d3a6c1642c55c067866fa0a51c2155ee33"}, + {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537e4baf0db67f382eb29255a03154fcd4984638303ff9baaa738b10371fa57"}, + {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8bfd36f368efe0ab2a6aa3db7f14598aac454b06849fb633b762ddbede1db90"}, + {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0877a9a2129a2c56a2eae2da016743db7d9d6a05d5e1c198f1b7808c602a30e"}, + {file = "greenlet-1.1.3-cp36-cp36m-win32.whl", hash = "sha256:88b04e12c9b041a1e0bcb886fec709c488192638a9a7a3677513ac6ba81d8e79"}, + {file = "greenlet-1.1.3-cp36-cp36m-win_amd64.whl", hash = "sha256:4f166b4aca8d7d489e82d74627a7069ab34211ef5ebb57c300ec4b9337b60fc0"}, + {file = "greenlet-1.1.3-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:cd16a89efe3a003029c87ff19e9fba635864e064da646bc749fc1908a4af18f3"}, + {file = "greenlet-1.1.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5b756e6730ea59b2745072e28ad27f4c837084688e6a6b3633c8b1e509e6ae0e"}, + {file = "greenlet-1.1.3-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:9b2f7d0408ddeb8ea1fd43d3db79a8cefaccadd2a812f021333b338ed6b10aba"}, + {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44b4817c34c9272c65550b788913620f1fdc80362b209bc9d7dd2f40d8793080"}, + {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d58a5a71c4c37354f9e0c24c9c8321f0185f6945ef027460b809f4bb474bfe41"}, + {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dd51d2650e70c6c4af37f454737bf4a11e568945b27f74b471e8e2a9fd21268"}, + {file = "greenlet-1.1.3-cp37-cp37m-win32.whl", hash = "sha256:048d2bed76c2aa6de7af500ae0ea51dd2267aec0e0f2a436981159053d0bc7cc"}, + {file = "greenlet-1.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:77e41db75f9958f2083e03e9dd39da12247b3430c92267df3af77c83d8ff9eed"}, + {file = "greenlet-1.1.3-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:1626185d938d7381631e48e6f7713e8d4b964be246073e1a1d15c2f061ac9f08"}, + {file = "greenlet-1.1.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:1ec2779774d8e42ed0440cf8bc55540175187e8e934f2be25199bf4ed948cd9e"}, + {file = "greenlet-1.1.3-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f2f908239b7098799b8845e5936c2ccb91d8c2323be02e82f8dcb4a80dcf4a25"}, + {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b181e9aa6cb2f5ec0cacc8cee6e5a3093416c841ba32c185c30c160487f0380"}, + {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cf45e339cabea16c07586306a31cfcc5a3b5e1626d365714d283732afed6809"}, + {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6200a11f003ec26815f7e3d2ded01b43a3810be3528dd760d2f1fa777490c3cd"}, + {file = "greenlet-1.1.3-cp38-cp38-win32.whl", hash = "sha256:db5b25265010a1b3dca6a174a443a0ed4c4ab12d5e2883a11c97d6e6d59b12f9"}, + {file = "greenlet-1.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:095a980288fe05adf3d002fbb180c99bdcf0f930e220aa66fcd56e7914a38202"}, + {file = "greenlet-1.1.3-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:cbc1eb55342cbac8f7ec159088d54e2cfdd5ddf61c87b8bbe682d113789331b2"}, + {file = "greenlet-1.1.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:694ffa7144fa5cc526c8f4512665003a39fa09ef00d19bbca5c8d3406db72fbe"}, + {file = "greenlet-1.1.3-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:aa741c1a8a8cc25eb3a3a01a62bdb5095a773d8c6a86470bde7f607a447e7905"}, + {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3a669f11289a8995d24fbfc0e63f8289dd03c9aaa0cc8f1eab31d18ca61a382"}, + {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76a53bfa10b367ee734b95988bd82a9a5f0038a25030f9f23bbbc005010ca600"}, + {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fb0aa7f6996879551fd67461d5d3ab0c3c0245da98be90c89fcb7a18d437403"}, + {file = "greenlet-1.1.3-cp39-cp39-win32.whl", hash = "sha256:5fbe1ab72b998ca77ceabbae63a9b2e2dc2d963f4299b9b278252ddba142d3f1"}, + {file = "greenlet-1.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:ffe73f9e7aea404722058405ff24041e59d31ca23d1da0895af48050a07b6932"}, + {file = "greenlet-1.1.3.tar.gz", hash = "sha256:bcb6c6dd1d6be6d38d6db283747d07fda089ff8c559a835236560a4410340455"}, ] hypothesis = [ {file = "hypothesis-4.57.1-py3-none-any.whl", hash = "sha256:94f0910bc87e0ae8c098f4ada28dfdc381245e0c8079c674292b417dbde144b5"}, {file = "hypothesis-4.57.1.tar.gz", hash = "sha256:3c4369a4b0a1348561048bcda5f1db951a1b8e2a514ea8e8c70d36e656bf6fa0"}, ] idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"}, - {file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"}, + {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"}, + {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] invoke = [ - {file = "invoke-1.7.1-py3-none-any.whl", hash = "sha256:2dc975b4f92be0c0a174ad2d063010c8a1fdb5e9389d69871001118b4fcac4fb"}, - {file = "invoke-1.7.1.tar.gz", hash = "sha256:7b6deaf585eee0a848205d0b8c0014b9bf6f287a8eb798818a642dff1df14b19"}, + {file = "invoke-1.7.3-py3-none-any.whl", hash = "sha256:d9694a865764dd3fd91f25f7e9a97fb41666e822bbb00e670091e3f43933574d"}, + {file = "invoke-1.7.3.tar.gz", hash = "sha256:41b428342d466a82135d5ab37119685a989713742be46e42a3a399d685579314"}, ] ipdb = [ {file = "ipdb-0.12.3.tar.gz", hash = "sha256:5d9a4a0e3b7027a158fc6f2929934341045b9c3b0b86ed5d7e84e409653f72fd"}, ] ipython = [ - {file = "ipython-7.16.3-py3-none-any.whl", hash = "sha256:c0427ed8bc33ac481faf9d3acf7e84e0010cdaada945e0badd1e2e74cc075833"}, - {file = "ipython-7.16.3.tar.gz", hash = "sha256:5ac47dc9af66fc2f5530c12069390877ae372ac905edca75a92a6e363b5d7caa"}, -] -ipython-genutils = [ - {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, - {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, + {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, + {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, ] jedi = [ {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, ] -jinja2 = [ - {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, - {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, +Jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] libtmux = [ {file = "libtmux-0.8.5-py3-none-any.whl", hash = "sha256:640b315497dba2235778d9fe6924e995523f61bfbbca35a0f8036fe07c1627b3"}, @@ -1539,93 +1567,64 @@ loguru = [ {file = "loguru-0.3.2-py3-none-any.whl", hash = "sha256:b6fad0d7aed357b5c147edcc6982606b933754338950b72d8123f48c150c5a4f"}, {file = "loguru-0.3.2.tar.gz", hash = "sha256:e3138bfdee5f57481a2a6e078714be20f8c71ab1ff3f07f8fb1cfa25191fed2a"}, ] -mako = [ - {file = "Mako-1.1.6-py2.py3-none-any.whl", hash = "sha256:afaf8e515d075b22fad7d7b8b30e4a1c90624ff2f3733a06ec125f5a5f043a57"}, - {file = "Mako-1.1.6.tar.gz", hash = "sha256:4e9e345a41924a954251b95b4b28e14a301145b544901332e658907a7464b6b2"}, -] -markdown = [ - {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, - {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, -] -markupsafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, - {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, +Mako = [ + {file = "Mako-1.2.3-py3-none-any.whl", hash = "sha256:c413a086e38cd885088d5e165305ee8eed04e8b3f8f62df343480da0a385735f"}, + {file = "Mako-1.2.3.tar.gz", hash = "sha256:7fde96466fcfeedb0eed94f187f20b23d85e4cb41444be0e542e2c8c65c396cd"}, +] +Markdown = [ + {file = "Markdown-3.4.1-py3-none-any.whl", hash = "sha256:08fb8465cffd03d10b9dd34a5c3fea908e20391a2a90b88d66362cb05beed186"}, + {file = "Markdown-3.4.1.tar.gz", hash = "sha256:3b809086bb6efad416156e00a0da66fe47618a5d6918dd688f53f40c8e4cfeff"}, +] +MarkupSafe = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] +matplotlib-inline = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] -more-itertools = [ - {file = "more-itertools-8.13.0.tar.gz", hash = "sha256:a42901a0a5b169d925f6f217cd5a190e32ef54360905b9c39ee7db5313bfec0f"}, - {file = "more_itertools-8.13.0-py3-none-any.whl", hash = "sha256:c5122bffc5f104d37c1626b8615b511f3427aa5389b94d61e5ef8236bfbc3ddb"}, -] msgpack = [ {file = "msgpack-0.6.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:774f5edc3475917cd95fe593e625d23d8580f9b48b570d8853d06cac171cd170"}, {file = "msgpack-0.6.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:a06efd0482a1942aad209a6c18321b5e22d64eb531ea20af138b28172d8f35ba"}, @@ -1688,8 +1687,8 @@ pickleshare = [ {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] pluggy = [ - {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, - {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] prompt-toolkit = [ {file = "prompt_toolkit-2.0.10-py2-none-any.whl", hash = "sha256:e7f8af9e3d70f514373bf41aa51bc33af12a6db3f71461ea47fea985defb2c31"}, @@ -1697,38 +1696,38 @@ prompt-toolkit = [ {file = "prompt_toolkit-2.0.10.tar.gz", hash = "sha256:f15af68f66e664eaa559d4ac8a928111eebd5feda0c11738b5998045224829db"}, ] psutil = [ - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, - {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, - {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, - {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, - {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, - {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, - {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, - {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, - {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, - {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, - {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, - {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, - {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, - {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, - {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, - {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, - {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, - {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, - {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"}, + {file = "psutil-5.9.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:8f024fbb26c8daf5d70287bb3edfafa22283c255287cf523c5d81721e8e5d82c"}, + {file = "psutil-5.9.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b2f248ffc346f4f4f0d747ee1947963613216b06688be0be2e393986fe20dbbb"}, + {file = "psutil-5.9.2-cp27-cp27m-win32.whl", hash = "sha256:b1928b9bf478d31fdffdb57101d18f9b70ed4e9b0e41af751851813547b2a9ab"}, + {file = "psutil-5.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:404f4816c16a2fcc4eaa36d7eb49a66df2d083e829d3e39ee8759a411dbc9ecf"}, + {file = "psutil-5.9.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:94e621c6a4ddb2573d4d30cba074f6d1aa0186645917df42c811c473dd22b339"}, + {file = "psutil-5.9.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:256098b4f6ffea6441eb54ab3eb64db9ecef18f6a80d7ba91549195d55420f84"}, + {file = "psutil-5.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:614337922702e9be37a39954d67fdb9e855981624d8011a9927b8f2d3c9625d9"}, + {file = "psutil-5.9.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39ec06dc6c934fb53df10c1672e299145ce609ff0611b569e75a88f313634969"}, + {file = "psutil-5.9.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3ac2c0375ef498e74b9b4ec56df3c88be43fe56cac465627572dbfb21c4be34"}, + {file = "psutil-5.9.2-cp310-cp310-win32.whl", hash = "sha256:e4c4a7636ffc47b7141864f1c5e7d649f42c54e49da2dd3cceb1c5f5d29bfc85"}, + {file = "psutil-5.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:f4cb67215c10d4657e320037109939b1c1d2fd70ca3d76301992f89fe2edb1f1"}, + {file = "psutil-5.9.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dc9bda7d5ced744622f157cc8d8bdd51735dafcecff807e928ff26bdb0ff097d"}, + {file = "psutil-5.9.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75291912b945a7351d45df682f9644540d564d62115d4a20d45fa17dc2d48f8"}, + {file = "psutil-5.9.2-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4018d5f9b6651f9896c7a7c2c9f4652e4eea53f10751c4e7d08a9093ab587ec"}, + {file = "psutil-5.9.2-cp36-cp36m-win32.whl", hash = "sha256:f40ba362fefc11d6bea4403f070078d60053ed422255bd838cd86a40674364c9"}, + {file = "psutil-5.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9770c1d25aee91417eba7869139d629d6328a9422ce1cdd112bd56377ca98444"}, + {file = "psutil-5.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:42638876b7f5ef43cef8dcf640d3401b27a51ee3fa137cb2aa2e72e188414c32"}, + {file = "psutil-5.9.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91aa0dac0c64688667b4285fa29354acfb3e834e1fd98b535b9986c883c2ce1d"}, + {file = "psutil-5.9.2-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fb54941aac044a61db9d8eb56fc5bee207db3bc58645d657249030e15ba3727"}, + {file = "psutil-5.9.2-cp37-cp37m-win32.whl", hash = "sha256:7cbb795dcd8ed8fd238bc9e9f64ab188f3f4096d2e811b5a82da53d164b84c3f"}, + {file = "psutil-5.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:5d39e3a2d5c40efa977c9a8dd4f679763c43c6c255b1340a56489955dbca767c"}, + {file = "psutil-5.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd331866628d18223a4265371fd255774affd86244fc307ef66eaf00de0633d5"}, + {file = "psutil-5.9.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b315febaebae813326296872fdb4be92ad3ce10d1d742a6b0c49fb619481ed0b"}, + {file = "psutil-5.9.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7929a516125f62399d6e8e026129c8835f6c5a3aab88c3fff1a05ee8feb840d"}, + {file = "psutil-5.9.2-cp38-cp38-win32.whl", hash = "sha256:561dec454853846d1dd0247b44c2e66a0a0c490f937086930ec4b8f83bf44f06"}, + {file = "psutil-5.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:67b33f27fc0427483b61563a16c90d9f3b547eeb7af0ef1b9fe024cdc9b3a6ea"}, + {file = "psutil-5.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3591616fa07b15050b2f87e1cdefd06a554382e72866fcc0ab2be9d116486c8"}, + {file = "psutil-5.9.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b29f581b5edab1f133563272a6011925401804d52d603c5c606936b49c8b97"}, + {file = "psutil-5.9.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4642fd93785a29353d6917a23e2ac6177308ef5e8be5cc17008d885cb9f70f12"}, + {file = "psutil-5.9.2-cp39-cp39-win32.whl", hash = "sha256:ed29ea0b9a372c5188cdb2ad39f937900a10fb5478dc077283bf86eeac678ef1"}, + {file = "psutil-5.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:68b35cbff92d1f7103d8f1db77c977e72f49fcefae3d3d2b91c76b0e7aef48b8"}, + {file = "psutil-5.9.2.tar.gz", hash = "sha256:feb861a10b6c3bb00701063b37e4afc754f8217f0f09c42280586bd6ac712b5c"}, ] ptpython = [ {file = "ptpython-2.0.6-py2.py3-none-any.whl", hash = "sha256:0977f56c934789d9955839ef71268148858f826fda49b267cb377ac7501a897b"}, @@ -1757,43 +1756,32 @@ pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] -pygments = [ - {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, - {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, +Pygments = [ + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] pylzma = [ {file = "pylzma-0.5.0.tar.gz", hash = "sha256:b874172afbf37770e643bf2dc9d9b6b03eb95d8f8162e157145b3fe9e1b68a1c"}, ] -pynacl = [ - {file = "PyNaCl-1.3.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621"}, - {file = "PyNaCl-1.3.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39"}, - {file = "PyNaCl-1.3.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255"}, - {file = "PyNaCl-1.3.0-cp27-cp27m-win32.whl", hash = "sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f"}, - {file = "PyNaCl-1.3.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0"}, - {file = "PyNaCl-1.3.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1"}, - {file = "PyNaCl-1.3.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e"}, - {file = "PyNaCl-1.3.0-cp34-abi3-macosx_10_6_intel.whl", hash = "sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1"}, - {file = "PyNaCl-1.3.0-cp34-abi3-manylinux1_i686.whl", hash = "sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786"}, - {file = "PyNaCl-1.3.0-cp34-abi3-manylinux1_x86_64.whl", hash = "sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415"}, - {file = "PyNaCl-1.3.0-cp34-cp34m-win32.whl", hash = "sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b"}, - {file = "PyNaCl-1.3.0-cp34-cp34m-win_amd64.whl", hash = "sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae"}, - {file = "PyNaCl-1.3.0-cp35-cp35m-win32.whl", hash = "sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310"}, - {file = "PyNaCl-1.3.0-cp35-cp35m-win_amd64.whl", hash = "sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a"}, - {file = "PyNaCl-1.3.0-cp36-cp36m-win32.whl", hash = "sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20"}, - {file = "PyNaCl-1.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b"}, - {file = "PyNaCl-1.3.0-cp37-cp37m-win32.whl", hash = "sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56"}, - {file = "PyNaCl-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715"}, - {file = "PyNaCl-1.3.0-cp38-cp38-win32.whl", hash = "sha256:53126cd91356342dcae7e209f840212a58dcf1177ad52c1d938d428eebc9fee5"}, - {file = "PyNaCl-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:bf459128feb543cfca16a95f8da31e2e65e4c5257d2f3dfa8c0c1031139c9c92"}, - {file = "PyNaCl-1.3.0.tar.gz", hash = "sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c"}, +PyNaCl = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, ] pyparsing = [ - {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, - {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] pytest = [ - {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, - {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, + {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, + {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, ] pytest-cov = [ {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, @@ -1821,7 +1809,7 @@ pywin32 = [ {file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"}, {file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"}, ] -pyyaml = [ +PyYAML = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, @@ -1857,13 +1845,17 @@ redis = [ {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"}, ] requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] secretconf = [ {file = "secretconf-0.1.2-py3-none-any.whl", hash = "sha256:e1b556b3de83852b9ae97a85b0041987968689efd6673f2cfbb5d5808c968e02"}, {file = "secretconf-0.1.2.tar.gz", hash = "sha256:89aa68c7bcd995aad277a0dadaaf9f92c2e22d56a17bc4f923a01077e097eb47"}, ] +setuptools = [ + {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"}, + {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1888,17 +1880,21 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] traitlets = [ - {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, - {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, + {file = "traitlets-5.4.0-py3-none-any.whl", hash = "sha256:93663cc8236093d48150e2af5e2ed30fc7904a11a6195e21bab0408af4e6d6c8"}, + {file = "traitlets-5.4.0.tar.gz", hash = "sha256:3f2c4e435e271592fe4390f1746ea56836e3a080f84e7833f0f801d9613fec39"}, ] typing-extensions = [ - {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, - {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] urllib3 = [ - {file = "urllib3-1.26.11-py2.py3-none-any.whl", hash = "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc"}, - {file = "urllib3-1.26.11.tar.gz", hash = "sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a"}, + {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, + {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, ] urwid = [ {file = "urwid-2.1.2.tar.gz", hash = "sha256:588bee9c1cb208d0906a9f73c613d2bd32c3ed3702012f51efe318a3f2127eae"}, @@ -1911,10 +1907,10 @@ wcwidth = [ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] websocket-client = [ - {file = "websocket-client-1.3.1.tar.gz", hash = "sha256:6278a75065395418283f887de7c3beafb3aa68dada5cacbe4b214e8d26da499b"}, - {file = "websocket_client-1.3.1-py3-none-any.whl", hash = "sha256:074e2ed575e7c822fc0940d31c3ac9bb2b1142c303eafcf3e304e6ce035522e8"}, + {file = "websocket-client-1.4.1.tar.gz", hash = "sha256:f9611eb65c8241a67fb373bef040b3cf8ad377a9f6546a12b620b6511e8ea9ef"}, + {file = "websocket_client-1.4.1-py3-none-any.whl", hash = "sha256:398909eb7e261f44b8f4bd474785b6ec5f5b499d4953342fe9755e01ef624090"}, ] -whoosh = [ +Whoosh = [ {file = "Whoosh-2.7.4-py2.py3-none-any.whl", hash = "sha256:aa39c3c3426e3fd107dcb4bde64ca1e276a65a889d9085a6e4b54ba82420a852"}, {file = "Whoosh-2.7.4.tar.gz", hash = "sha256:7ca5633dbfa9e0e0fa400d3151a8a0c4bec53bd2ecedc0a67705b17565c31a83"}, {file = "Whoosh-2.7.4.zip", hash = "sha256:e0857375f63e9041e03fedd5b7541f97cf78917ac1b6b06c1fcc9b45375dda69"}, @@ -1924,8 +1920,8 @@ win32-setctime = [ {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, ] zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, + {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"}, + {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"}, ] "zope.event" = [ {file = "zope.event-4.5.0-py2.py3-none-any.whl", hash = "sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42"}, diff --git a/pyproject.toml b/pyproject.toml index 2e0938044..1c1a38328 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,19 +1,19 @@ [tool.poetry] name = "js-ng" packages = [{ include = "jumpscale" }] -version = "11.0b15" +version = "11.0b16" description = "system automation, configuration management and RPC framework" authors = ["xmonader "] license = "MIT" [tool.poetry.dependencies] -python = "^3.6" +python = ">=3.7,<4.0" pdoc3 = "^0.6.3" pytoml = "^0.1.21" secretconf = "^0.1.2" better-exceptions = "^0.2.2" -pynacl = "1.3.0" -click = "^7.0" +PyNaCl = "^1.5.0" +click = "^8.1.3" ptpython = "^2.0" jedi = "0.17.2" loguru = "^0.3.2" @@ -33,7 +33,7 @@ dill = "^0.3.0" watchdog = "^0.9.0" GitPython = "^3.0" docker = "^4.2.0" -jinja2 = "^2.11.1" +Jinja2 = "^3.1.2" psutil = "^5.7.0" pudb = "^2019.1" prompt-toolkit = "<3.0.0" @@ -41,6 +41,7 @@ bottle = "^0.12.18" Whoosh= "^2.7.4" pycparser = "^2.20" zipp = "^3.1.0" +requests = "^2.28.1" [tool.poetry.dev-dependencies] ipython = "^7.6" @@ -48,7 +49,7 @@ ipdb = "^0.12.1" black = {version = "^18.3-alpha.0", allow-prereleases = true} flake8 = "^3.7" parameterized = "^0.7.0" -pytest = "^5.0" +pytest = "^7.1.3" codecov = "^2.0" hypothesis = "^4.28" pytest-cov = "^2.7" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 5b54414f9..000000000 --- a/requirements.txt +++ /dev/null @@ -1,375 +0,0 @@ -argh==0.26.2 \ - --hash=sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3 \ - --hash=sha256:e9535b8c84dc9571a48999094fda7f33e63c3f1b74f3e5f3ac0105a58405bb65 -arrow==0.15.8; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ - --hash=sha256:271b8e05174d48e50324ed0dc5d74796c839c7e579a4f21cf1a7394665f9e94f \ - --hash=sha256:edc31dc051db12c95da9bac0271cd1027b8e36912daf6d4580af53b23e62721a -bcrypt==3.2.0; python_version >= "3.6" \ - --hash=sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6 \ - --hash=sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7 \ - --hash=sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1 \ - --hash=sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d \ - --hash=sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55 \ - --hash=sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34 \ - --hash=sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29 -better-exceptions==0.2.2 \ - --hash=sha256:bf79c87659bc849989d726bf0e4a2100edefe7eded112d201f54fe08467fdf63 \ - --hash=sha256:c196cad849de615abb9f6eb67ca1b83f33b938818f0e2fe8fa157b22aeb7b992 -bottle==0.12.18 \ - --hash=sha256:43157254e88f32c6be16f8d9eb1f1d1472396a4e174ebd2bf62544854ecf37e7 \ - --hash=sha256:0819b74b145a7def225c0e83b16a4d5711fde751cd92bae467a69efce720f69e -certifi==2019.11.28; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3 \ - --hash=sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f -cffi==1.14.3; sys_platform == "win32" and platform_python_implementation == "CPython" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0") and python_version >= "3.6" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0") \ - --hash=sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc \ - --hash=sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768 \ - --hash=sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d \ - --hash=sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1 \ - --hash=sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca \ - --hash=sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a \ - --hash=sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c \ - --hash=sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730 \ - --hash=sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d \ - --hash=sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05 \ - --hash=sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b \ - --hash=sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171 \ - --hash=sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f \ - --hash=sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4 \ - --hash=sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d \ - --hash=sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d \ - --hash=sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3 \ - --hash=sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808 \ - --hash=sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537 \ - --hash=sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0 \ - --hash=sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e \ - --hash=sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1 \ - --hash=sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579 \ - --hash=sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394 \ - --hash=sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc \ - --hash=sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869 \ - --hash=sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e \ - --hash=sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828 \ - --hash=sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9 \ - --hash=sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522 \ - --hash=sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15 \ - --hash=sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d \ - --hash=sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c \ - --hash=sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b \ - --hash=sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3 \ - --hash=sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591 -chardet==3.0.4; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 \ - --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae -click==7.1.2; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ - --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc \ - --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a -colorama==0.4.3; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ - --hash=sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff \ - --hash=sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1 -cryptography==3.2.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:6dc59630ecce8c1f558277ceb212c751d6730bd12c80ea96b4ac65637c4f55e7 \ - --hash=sha256:75e8e6684cf0034f6bf2a97095cb95f81537b12b36a8fedf06e73050bb171c2d \ - --hash=sha256:4e7268a0ca14536fecfdf2b00297d4e407da904718658c1ff1961c713f90fd33 \ - --hash=sha256:7117319b44ed1842c617d0a452383a5a052ec6aa726dfbaffa8b94c910444297 \ - --hash=sha256:a733671100cd26d816eed39507e585c156e4498293a907029969234e5e634bc4 \ - --hash=sha256:a75f306a16d9f9afebfbedc41c8c2351d8e61e818ba6b4c40815e2b5740bb6b8 \ - --hash=sha256:5849d59358547bf789ee7e0d7a9036b2d29e9a4ddf1ce5e06bb45634f995c53e \ - --hash=sha256:bd717aa029217b8ef94a7d21632a3bb5a4e7218a4513d2521c2a2fd63011e98b \ - --hash=sha256:efe15aca4f64f3a7ea0c09c87826490e50ed166ce67368a68f315ea0807a20df \ - --hash=sha256:32434673d8505b42c0de4de86da8c1620651abd24afe91ae0335597683ed1b77 \ - --hash=sha256:7b8d9d8d3a9bd240f453342981f765346c87ade811519f98664519696f8e6ab7 \ - --hash=sha256:d3545829ab42a66b84a9aaabf216a4dce7f16dbc76eb69be5c302ed6b8f4a29b \ - --hash=sha256:a4e27ed0b2504195f855b52052eadcc9795c59909c9d84314c5408687f933fc7 \ - --hash=sha256:13b88a0bd044b4eae1ef40e265d006e34dbcde0c2f1e15eb9896501b2d8f6c6f \ - --hash=sha256:07ca431b788249af92764e3be9a488aa1d39a0bc3be313d826bbec690417e538 \ - --hash=sha256:a035a10686532b0587d58a606004aa20ad895c60c4d029afa245802347fab57b \ - --hash=sha256:d26a2557d8f9122f9bf445fc7034242f4375bd4e95ecda007667540270965b13 \ - --hash=sha256:545a8550782dda68f8cdc75a6e3bf252017aa8f75f19f5a9ca940772fc0cb56e \ - --hash=sha256:55d0b896631412b6f0c7de56e12eb3e261ac347fbaa5d5e705291a9016e5f8cb \ - --hash=sha256:3cd75a683b15576cfc822c7c5742b3276e50b21a06672dc3a800a2d5da4ecd1b \ - --hash=sha256:d25cecbac20713a7c3bc544372d42d8eafa89799f492a43b79e1dfd650484851 \ - --hash=sha256:d3d5e10be0cf2a12214ddee45c6bd203dab435e3d83b4560c03066eda600bfe3 -dill==0.3.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.1.0") \ - --hash=sha256:6e12da0d8e49c220e8d6e97ee8882002e624f1160289ce85ec2cc0a5246b3a2e -distro==1.5.0 \ - --hash=sha256:df74eed763e18d10d0da624258524ae80486432cd17392d9c3d96f5e83cd2799 \ - --hash=sha256:0e58756ae38fbd8fc3020d54badb8eae17c5b9dcbed388b17bb55b8a5928df92 -docker==4.3.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ - --hash=sha256:13966471e8bc23b36bfb3a6fb4ab75043a5ef1dac86516274777576bed3b9828 \ - --hash=sha256:bad94b8dd001a8a4af19ce4becc17f41b09f228173ffe6a4e0355389eef142f2 -docopt==0.6.2 \ - --hash=sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491 -fabric==2.5.0 \ - --hash=sha256:160331934ea60036604928e792fa8e9f813266b098ef5562aa82b88527740389 \ - --hash=sha256:24842d7d51556adcabd885ac3cf5e1df73fc622a1708bf3667bf5927576cdfa6 -faker==2.0.5; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ - --hash=sha256:c1d19f8c63487b4711189403f5c2e06af1dd8b5be7928f6547f5f7cddd13d917 \ - --hash=sha256:29093e61f12745150774fd05ab499e87252a4fa51ed51e45c40179e854f87925 -gevent==1.4.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") \ - --hash=sha256:1b7d3a285978b27b469c0ff5fb5a72bcd69f4306dbbf22d7997d83209a8ba917 \ - --hash=sha256:44089ed06a962a3a70e96353c981d628b2d4a2f2a75ea5d90f916a62d22af2e8 \ - --hash=sha256:0e1e5b73a445fe82d40907322e1e0eec6a6745ca3cea19291c6f9f50117bb7ea \ - --hash=sha256:74b7528f901f39c39cdbb50cdf08f1a2351725d9aebaef212a29abfbb06895ee \ - --hash=sha256:0ff2b70e8e338cf13bedf146b8c29d475e2a544b5d1fe14045aee827c073842c \ - --hash=sha256:0774babec518a24d9a7231d4e689931f31b332c4517a771e532002614e270a64 \ - --hash=sha256:d752bcf1b98174780e2317ada12013d612f05116456133a6acf3e17d43b71f05 \ - --hash=sha256:3249011d13d0c63bea72d91cec23a9cf18c25f91d1f115121e5c9113d753fa12 \ - --hash=sha256:d1e6d1f156e999edab069d79d890859806b555ce4e4da5b6418616322f0a3df1 \ - --hash=sha256:7d0809e2991c9784eceeadef01c27ee6a33ca09ebba6154317a257353e3af922 \ - --hash=sha256:14b4d06d19d39a440e72253f77067d27209c67e7611e352f79fe69e0f618f76e \ - --hash=sha256:53b72385857e04e7faca13c613c07cab411480822ac658d97fd8a4ddbaf715c8 \ - --hash=sha256:8d9ec51cc06580f8c21b41fd3f2b3465197ba5b23c00eb7d422b7ae0380510b0 \ - --hash=sha256:2711e69788ddb34c059a30186e05c55a6b611cb9e34ac343e69cf3264d42fe1c \ - --hash=sha256:e5bcc4270671936349249d26140c267397b7b4b1381f5ec8b13c53c5b53ab6e1 \ - --hash=sha256:9f7a1e96fec45f70ad364e46de32ccacab4d80de238bd3c2edd036867ccd48ad \ - --hash=sha256:50024a1ee2cf04645535c5ebaeaa0a60c5ef32e262da981f4be0546b26791950 \ - --hash=sha256:4bfa291e3c931ff3c99a349d8857605dca029de61d74c6bb82bd46373959c942 \ - --hash=sha256:ab4dc33ef0e26dc627559786a4fba0c2227f125db85d970abbf85b77506b3f51 \ - --hash=sha256:896b2b80931d6b13b5d9feba3d4eebc67d5e6ec54f0cf3339d08487d55d93b0e \ - --hash=sha256:107f4232db2172f7e8429ed7779c10f2ed16616d75ffbe77e0e0c3fcdeb51a51 \ - --hash=sha256:28a0c5417b464562ab9842dd1fb0cc1524e60494641d973206ec24d6ec5f6909 \ - --hash=sha256:1eb7fa3b9bd9174dfe9c3b59b7a09b768ecd496debfc4976a9530a3e15c990d1 -gitdb==4.0.5; python_version >= "3.4" \ - --hash=sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac \ - --hash=sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9 -gitpython==3.1.8; python_version >= "3.4" \ - --hash=sha256:1858f4fd089abe92ae465f01d5aaaf55e937eca565fb2c1fce35a51b5f85c910 \ - --hash=sha256:080bf8e2cf1a2b907634761c2eaefbe83b69930c94c66ad11b65a8252959f912 -greenlet==0.4.16 \ - --hash=sha256:80cb0380838bf4e48da6adedb0c7cd060c187bb4a75f67a5aa9ec33689b84872 \ - --hash=sha256:df7de669cbf21de4b04a3ffc9920bc8426cab4c61365fa84d79bf97401a8bef7 \ - --hash=sha256:1429dc183b36ec972055e13250d96e174491559433eb3061691b446899b87384 \ - --hash=sha256:5ea034d040e6ab1d2ae04ab05a3f37dbd719c4dee3804b13903d4cc794b1336e \ - --hash=sha256:c196a5394c56352e21cb7224739c6dd0075b69dd56f758505951d1d8d68cf8a9 \ - --hash=sha256:1000038ba0ea9032948e2156a9c15f5686f36945e8f9906e6b8db49f358e7b52 \ - --hash=sha256:1b805231bfb7b2900a16638c3c8b45c694334c811f84463e52451e00c9412691 \ - --hash=sha256:e5db19d4a7d41bbeb3dd89b49fc1bc7e6e515b51bbf32589c618655a0ebe0bf0 \ - --hash=sha256:eac2a3f659d5f41d6bbfb6a97733bc7800ea5e906dc873732e00cebb98cec9e4 \ - --hash=sha256:7eed31f4efc8356e200568ba05ad645525f1fbd8674f1e5be61a493e715e3873 \ - --hash=sha256:682328aa576ec393c1872615bcb877cf32d800d4a2f150e1a5dc7e56644010b1 \ - --hash=sha256:3a35e33902b2e6079949feed7a2dafa5ac6f019da97bd255842bb22de3c11bf5 \ - --hash=sha256:b0b2a984bbfc543d144d88caad6cc7ff4a71be77102014bd617bd88cfb038727 \ - --hash=sha256:d83c1d38658b0f81c282b41238092ed89d8f93c6e342224ab73fb39e16848721 \ - --hash=sha256:e695ac8c3efe124d998230b219eb51afb6ef10524a50b3c45109c4b77a8a3a92 \ - --hash=sha256:133ba06bad4e5f2f8bf6a0ac434e0fd686df749a86b3478903b92ec3a9c0c90b \ - --hash=sha256:6e06eac722676797e8fce4adb8ad3dc57a1bb3adfb0dd3fdf8306c055a38456c -idna==2.10; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 \ - --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 -importlib-metadata==2.0.0; python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_version < "3.8" and python_version >= "3.5" and python_full_version >= "3.5.0" \ - --hash=sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3 \ - --hash=sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da -invoke==1.4.1 \ - --hash=sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134 \ - --hash=sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132 \ - --hash=sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d -jedi==0.17.2; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5 \ - --hash=sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20 -jinja2==2.11.2; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ - --hash=sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035 \ - --hash=sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0 -libtmux==0.8.3 \ - --hash=sha256:b628121f6db98e34837f78e06aa4388c1a0c53518b2336f437b023bd95798c09 \ - --hash=sha256:a4c7379604ccdc684aa865723211184709f9a2b45511772989b5f26ad156650e -loguru==0.3.2; python_version >= "3.5" \ - --hash=sha256:b6fad0d7aed357b5c147edcc6982606b933754338950b72d8123f48c150c5a4f \ - --hash=sha256:e3138bfdee5f57481a2a6e078714be20f8c71ab1ff3f07f8fb1cfa25191fed2a -mako==1.1.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" \ - --hash=sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9 \ - --hash=sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27 -markdown==3.2.2; python_version >= "3.5" \ - --hash=sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59 \ - --hash=sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17 -markupsafe==1.1.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" \ - --hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \ - --hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7 \ - --hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \ - --hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \ - --hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \ - --hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \ - --hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \ - --hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \ - --hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \ - --hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \ - --hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \ - --hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \ - --hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \ - --hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \ - --hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \ - --hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \ - --hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \ - --hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \ - --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \ - --hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \ - --hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \ - --hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \ - --hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \ - --hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \ - --hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \ - --hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \ - --hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \ - --hash=sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15 \ - --hash=sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2 \ - --hash=sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42 \ - --hash=sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b \ - --hash=sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be \ - --hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b -msgpack==0.6.2 \ - --hash=sha256:774f5edc3475917cd95fe593e625d23d8580f9b48b570d8853d06cac171cd170 \ - --hash=sha256:a06efd0482a1942aad209a6c18321b5e22d64eb531ea20af138b28172d8f35ba \ - --hash=sha256:8a3ada8401736df2bf497f65589293a86c56e197a80ae7634ec2c3150a2f5082 \ - --hash=sha256:b8b4bd3dafc7b92608ae5462add1c8cc881851c2d4f5d8977fdea5b081d17f21 \ - --hash=sha256:24149a75643aeaa81ece4259084d11b792308a6cf74e796cbb35def94c89a25a \ - --hash=sha256:757bd71a9b89e4f1db0622af4436d403e742506dbea978eba566815dc65ec895 \ - --hash=sha256:32fea0ea3cd1ef820286863a6202dcfd62a539b8ec3edcbdff76068a8c2cc6ce \ - --hash=sha256:db7ff14abc73577b0bcbcf73ecff97d3580ecaa0fc8724babce21fdf3fe08ef6 \ - --hash=sha256:187794cd1eb73acccd528247e3565f6760bd842d7dc299241f830024a7dd5610 \ - --hash=sha256:b24afc52e18dccc8c175de07c1d680bdf315844566f4952b5bedb908894bec79 \ - --hash=sha256:355f7fd0f90134229eaeefaee3cf42e0afc8518e8f3cd4b25f541a7104dcb8f9 \ - --hash=sha256:76df51492bc6fa6cc8b65d09efdb67cbba3cbfe55004c3afc81352af92b4a43c \ - --hash=sha256:f0f47bafe9c9b8ed03e19a100a743662dd8c6d0135e684feea720a0d0046d116 \ - --hash=sha256:c6e5024fc0cdf7f83b6624850309ddd7e06c48a75fa0d1c5173de4d93300eb19 \ - --hash=sha256:30b88c47e0cdb6062daed88ca283b0d84fa0d2ad6c273aa0788152a1c643e408 \ - --hash=sha256:229a0ccdc39e9b6c6d1033cd8aecd9c296823b6c87f0de3943c59b8bc7c64bee \ - --hash=sha256:4abdb88a9b67e64810fb54b0c24a1fd76b12297b4f7a1467d85a14dd8367191a \ - --hash=sha256:dedf54d72d9e7b6d043c244c8213fe2b8bbfe66874b9a65b39c4cc892dd99dd4 \ - --hash=sha256:0cc7ca04e575ba34fea7cfcd76039f55def570e6950e4155a4174368142c8e1b \ - --hash=sha256:1904b7cb65342d0998b75908304a03cb004c63ef31e16c8c43fee6b989d7f0d7 \ - --hash=sha256:ea3c2f859346fcd55fc46e96885301d9c2f7a36d453f5d8f2967840efa1e1830 -paramiko==2.7.2 \ - --hash=sha256:4f3e316fef2ac628b05097a637af35685183111d4bc1b5979bd397c2ab7b5898 \ - --hash=sha256:7f36f4ba2c0d81d219f4595e35f70d56cc94f9ac40a6acdf51d6ca210ce65035 -parso==0.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea \ - --hash=sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9 -pathtools==0.1.2 \ - --hash=sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0 -pdoc3==0.6.4; python_version >= "3.5" \ - --hash=sha256:85cbb0de17d1306157d19b08b67ad84817098c12ad9f92ec203b79d0307b6a25 -prompt-toolkit==2.0.10; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") \ - --hash=sha256:e7f8af9e3d70f514373bf41aa51bc33af12a6db3f71461ea47fea985defb2c31 \ - --hash=sha256:46642344ce457641f28fc9d1c9ca939b63dadf8df128b86f1b9860e59c73a5e4 \ - --hash=sha256:f15af68f66e664eaa559d4ac8a928111eebd5feda0c11738b5998045224829db -psutil==5.7.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") \ - --hash=sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2 \ - --hash=sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195 \ - --hash=sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c \ - --hash=sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6 \ - --hash=sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf \ - --hash=sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8 \ - --hash=sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818 \ - --hash=sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1 \ - --hash=sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498 \ - --hash=sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f \ - --hash=sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb -ptpython==2.0.6 \ - --hash=sha256:0977f56c934789d9955839ef71268148858f826fda49b267cb377ac7501a897b \ - --hash=sha256:90e24040e82de4abae0bbe6e352d59ae6657e14e1154e742c0038679361b052f -pudb==2019.2 \ - --hash=sha256:e8f0ea01b134d802872184b05bffc82af29a1eb2f9374a277434b932d68f58dc -pycparser==2.20; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") \ - --hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705 \ - --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 -pygments==2.7.1; python_version >= "3.5" \ - --hash=sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998 \ - --hash=sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7 -pylzma==0.5.0 \ - --hash=sha256:b874172afbf37770e643bf2dc9d9b6b03eb95d8f8162e157145b3fe9e1b68a1c -pynacl==1.3.0 \ - --hash=sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621 \ - --hash=sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39 \ - --hash=sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255 \ - --hash=sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f \ - --hash=sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0 \ - --hash=sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1 \ - --hash=sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e \ - --hash=sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1 \ - --hash=sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786 \ - --hash=sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415 \ - --hash=sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b \ - --hash=sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae \ - --hash=sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310 \ - --hash=sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a \ - --hash=sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20 \ - --hash=sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b \ - --hash=sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56 \ - --hash=sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715 \ - --hash=sha256:53126cd91356342dcae7e209f840212a58dcf1177ad52c1d938d428eebc9fee5 \ - --hash=sha256:bf459128feb543cfca16a95f8da31e2e65e4c5257d2f3dfa8c0c1031139c9c92 \ - --hash=sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c -python-dateutil==2.8.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c \ - --hash=sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a -pytoml==0.1.21 \ - --hash=sha256:57a21e6347049f73bfb62011ff34cd72774c031b9828cb628a752225136dfc33 \ - --hash=sha256:8eecf7c8d0adcff3b375b09fe403407aa9b645c499e5ab8cac670ac4a35f61e7 -pywin32==227; python_version >= "2.7" and python_full_version < "3.0.0" and sys_platform == "win32" or python_full_version >= "3.5.0" and sys_platform == "win32" \ - --hash=sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0 \ - --hash=sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116 \ - --hash=sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa \ - --hash=sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4 \ - --hash=sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be \ - --hash=sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2 \ - --hash=sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507 \ - --hash=sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511 \ - --hash=sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc \ - --hash=sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e \ - --hash=sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295 \ - --hash=sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c -pyyaml==5.3.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ - --hash=sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f \ - --hash=sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76 \ - --hash=sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2 \ - --hash=sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c \ - --hash=sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2 \ - --hash=sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648 \ - --hash=sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a \ - --hash=sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf \ - --hash=sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97 \ - --hash=sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee \ - --hash=sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d -redis==3.5.3; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ - --hash=sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24 \ - --hash=sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2 -requests==2.24.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898 \ - --hash=sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b -secretconf==0.1.2 \ - --hash=sha256:e1b556b3de83852b9ae97a85b0041987968689efd6673f2cfbb5d5808c968e02 \ - --hash=sha256:89aa68c7bcd995aad277a0dadaaf9f92c2e22d56a17bc4f923a01077e097eb47 -six==1.15.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" \ - --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced \ - --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 -smmap==3.0.4; python_version >= "3.4" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.4" \ - --hash=sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4 \ - --hash=sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24 -terminaltables==3.1.0 \ - --hash=sha256:f3eb0eb92e3833972ac36796293ca0906e998dc3be91fbe1f8615b331b853b81 -text-unidecode==1.3; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93 \ - --hash=sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8 -urllib3==1.25.10; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" \ - --hash=sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461 \ - --hash=sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a -urwid==2.1.1 \ - --hash=sha256:7870866e35b00b71b0c9ccdd1281c8e7fac3806d60b9c1075c95dd5dad88d526 -watchdog==0.9.0 \ - --hash=sha256:965f658d0732de3188211932aeb0bb457587f04f63ab4c1e33eab878e9de961d -wcwidth==0.2.5; python_version >= "2.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" \ - --hash=sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784 \ - --hash=sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83 -websocket-client==0.57.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ - --hash=sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549 \ - --hash=sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010 -whoosh==2.7.4 \ - --hash=sha256:aa39c3c3426e3fd107dcb4bde64ca1e276a65a889d9085a6e4b54ba82420a852 \ - --hash=sha256:7ca5633dbfa9e0e0fa400d3151a8a0c4bec53bd2ecedc0a67705b17565c31a83 \ - --hash=sha256:e0857375f63e9041e03fedd5b7541f97cf78917ac1b6b06c1fcc9b45375dda69 -win32-setctime==1.0.2; sys_platform == "win32" and python_version >= "3.5" \ - --hash=sha256:02b4c5959ca0b195f45c98115826c6e8a630b7cf648e724feaab1a5aa6250640 \ - --hash=sha256:47aa7c43548c1fc0a4f026d1944b748b37036df116c7c4cf908e82638d854313 -zipp==3.2.0; python_version >= "3.6" \ - --hash=sha256:43f4fa8d8bb313e65d8323a3952ef8756bf40f9a5c3ea7334be23ee4ec8278b6 \ - --hash=sha256:b52f22895f4cfce194bc8172f3819ee8de7540aa6d873535a8668b730b8b411f diff --git a/threesdk.spec b/threesdk.spec deleted file mode 100644 index 537a0a5f8..000000000 --- a/threesdk.spec +++ /dev/null @@ -1,36 +0,0 @@ -# -*- mode: python ; coding: utf-8 -*- -import wcwidth - -block_cipher = None - - -a = Analysis(['jumpscale/entry_points/usershell.py'], - pathex=['/tmp'], - binaries=[], - datas=[ - (os.path.dirname(wcwidth.__file__), 'wcwidth') - ], - hiddenimports = ["packaging.requirements", "pkg_resources.py2_warn", "pathlib", "_cffi_backend"], - hookspath=[], - runtime_hooks=[], - excludes=["tcl", "tkinter"], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, - noarchive=False) -pyz = PYZ(a.pure, a.zipped_data, - cipher=block_cipher) -exe = EXE(pyz, - a.scripts, - a.binaries, - a.zipfiles, - a.datas, - [], - name='3sdk', - debug=False, - bootloader_ignore_signals=False, - strip=True, - upx=True, - upx_exclude=[], - runtime_tmpdir=None, - console=True ) From 2502990ac4ab2133b4a6b0e1e14a03ddb35c3571 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ghanem Date: Mon, 10 Oct 2022 19:43:40 +0200 Subject: [PATCH 2/8] support for py3.10 (#608) * platform: use json.dumps instead of saferepr... also update black to latest * update black --- jumpscale/data/platform/__init__.py | 60 ++----------- poetry.lock | 131 +++++++++++++++++++++++----- pyproject.toml | 2 +- 3 files changed, 115 insertions(+), 78 deletions(-) diff --git a/jumpscale/data/platform/__init__.py b/jumpscale/data/platform/__init__.py index 6d3f88fbd..41b61c496 100644 --- a/jumpscale/data/platform/__init__.py +++ b/jumpscale/data/platform/__init__.py @@ -84,7 +84,7 @@ import os import sys import time -import pprint +import json import random import socket import struct @@ -283,63 +283,13 @@ def get_profile(scrub=False): return ret -_real_safe_repr = pprint._safe_repr - - -def _fake_json_dumps(val, indent=2): - # never do this. this is a hack for Python 2.4. Python 2.5 added - # the json module for a reason. - def _fake_safe_repr(*a, **kw): - res, is_read, is_rec = _real_safe_repr(*a, **kw) - if res == "None": - res = "null" - if res == "True": - res = "true" - if res == "False": - res = "false" - if not (res.startswith("'") or res.startswith("u'")): - res = res - else: - if res.startswith("u"): - res = res[1:] - - contents = res[1:-1] - contents = contents.replace('"', "").replace(r"\"", "") - res = '"' + contents + '"' - return res, is_read, is_rec - - pprint._safe_repr = _fake_safe_repr - try: - ret = pprint.pformat(val, indent=indent) - finally: - pprint._safe_repr = _real_safe_repr - - return ret - - def get_profile_json(indent=False): + data_dict = get_profile() + if indent: - indent = 2 + return json.dumps(data_dict, sort_keys=True, indent=2) else: - indent = 0 - try: - import json - - def dumps(val, indent): - if indent: - return json.dumps(val, sort_keys=True, indent=indent) - return json.dumps(val, sort_keys=True) - - except ImportError: - - def dumps(val, indent): - ret = _fake_json_dumps(val, indent=indent) - if not indent: - ret = re.sub("\n\s*", " ", ret) - return ret - - data_dict = get_profile() - return dumps(data_dict, indent) + return json.dumps(data_dict, sort_keys=True) def _escape_shell_args(args, sep=" ", style=None): diff --git a/poetry.lock b/poetry.lock index dc62849c2..5e8ac9f97 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,11 +1,3 @@ -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "appnope" version = "0.1.3" @@ -80,20 +72,26 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} [[package]] name = "black" -version = "18.9b0" +version = "22.10.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -appdirs = "*" -attrs = ">=17.4.0" -click = ">=6.5" -toml = ">=0.9.4" +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] -d = ["aiohttp (>=3.3.2)"] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "bottle" @@ -588,6 +586,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "objgraph" version = "3.5.0" @@ -663,6 +669,14 @@ python-versions = "*" [package.dependencies] six = "*" +[[package]] +name = "pathspec" +version = "0.10.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "pathtools" version = "0.1.2" @@ -702,6 +716,18 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + [[package]] name = "pluggy" version = "1.0.0" @@ -1043,6 +1069,14 @@ python-versions = ">=3.7" [package.extras] test = ["pre-commit", "pytest"] +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "typing-extensions" version = "4.4.0" @@ -1171,13 +1205,9 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = ">=3.7,<4.0" -content-hash = "56d9425d3c1b93c2df9697511d59a8a1471a7fea0b28e917bb00f0e934a004ce" +content-hash = "b24aea3b56d8c2a0a08f57ac5ead41759296250bf44dd36b65e52751afc38e9d" [metadata.files] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] appnope = [ {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, @@ -1217,8 +1247,27 @@ better-exceptions = [ {file = "better_exceptions-0.2.3.tar.gz", hash = "sha256:28184b6606d080d759ce79c738367254de2ca456eca349dcbab2514e2a865534"}, ] black = [ - {file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"}, - {file = "black-18.9b0.tar.gz", hash = "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"}, + {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, + {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, + {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, + {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, + {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, + {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, + {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, + {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, + {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, + {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, + {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, + {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, + {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, + {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, + {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, + {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, + {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, + {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, + {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, + {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, + {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, ] bottle = [ {file = "bottle-0.12.23-py3-none-any.whl", hash = "sha256:9f1c363257c590bd34db5fad4693a7f06ff4217e9ad18337451de69c25137127"}, @@ -1648,6 +1697,10 @@ msgpack = [ {file = "msgpack-0.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:1904b7cb65342d0998b75908304a03cb004c63ef31e16c8c43fee6b989d7f0d7"}, {file = "msgpack-0.6.2.tar.gz", hash = "sha256:ea3c2f859346fcd55fc46e96885301d9c2f7a36d453f5d8f2967840efa1e1830"}, ] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] objgraph = [ {file = "objgraph-3.5.0-py2.py3-none-any.whl", hash = "sha256:deb821bc51a88ff103893aeeee2a8965eb0f719af2a363f49d63d894938b50b6"}, {file = "objgraph-3.5.0.tar.gz", hash = "sha256:4752ca5bcc0e0512e41b8cc4d2780ac2fd3b3eabd03b7e950a5594c06203dfc4"}, @@ -1672,6 +1725,10 @@ pathlib2 = [ {file = "pathlib2-2.3.7.post1-py2.py3-none-any.whl", hash = "sha256:5266a0fd000452f1b3467d782f079a4343c63aaa119221fbdc4e39577489ca5b"}, {file = "pathlib2-2.3.7.post1.tar.gz", hash = "sha256:9fe0edad898b83c0c3e199c842b27ed216645d2e177757b2dd67384d4113c641"}, ] +pathspec = [ + {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, + {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, +] pathtools = [ {file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"}, ] @@ -1686,6 +1743,10 @@ pickleshare = [ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] +platformdirs = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, @@ -1888,6 +1949,32 @@ traitlets = [ {file = "traitlets-5.4.0-py3-none-any.whl", hash = "sha256:93663cc8236093d48150e2af5e2ed30fc7904a11a6195e21bab0408af4e6d6c8"}, {file = "traitlets-5.4.0.tar.gz", hash = "sha256:3f2c4e435e271592fe4390f1746ea56836e3a080f84e7833f0f801d9613fec39"}, ] +typed-ast = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] typing-extensions = [ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, diff --git a/pyproject.toml b/pyproject.toml index 1c1a38328..f68b12fa9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ requests = "^2.28.1" [tool.poetry.dev-dependencies] ipython = "^7.6" ipdb = "^0.12.1" -black = {version = "^18.3-alpha.0", allow-prereleases = true} +black = "^22.10.0" flake8 = "^3.7" parameterized = "^0.7.0" pytest = "^7.1.3" From cb51118fd646b1335c9b72731dd7d1b5c6c7b2f6 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ghanem Date: Mon, 10 Oct 2022 20:54:32 +0200 Subject: [PATCH 3/8] update pdoc3 and watchdog (#609) * update pdoc3 and watchdog * regenerate docs --- docs/api/jumpscale/clients/base.html | 40 +- docs/api/jumpscale/clients/docker/docker.html | 167 ++-- docs/api/jumpscale/clients/docker/index.html | 34 +- docs/api/jumpscale/clients/git/git.html | 104 ++- docs/api/jumpscale/clients/git/index.html | 32 +- docs/api/jumpscale/clients/index.html | 34 +- docs/api/jumpscale/clients/redis/index.html | 32 +- docs/api/jumpscale/clients/redis/redis.html | 86 +- .../jumpscale/clients/sshclient/index.html | 32 +- .../clients/sshclient/sshclient.html | 130 +-- docs/api/jumpscale/clients/sshkey/index.html | 34 +- docs/api/jumpscale/clients/sshkey/sshkey.html | 132 +-- docs/api/jumpscale/clients/zdb/client.html | 268 ++++--- docs/api/jumpscale/clients/zdb/index.html | 40 +- .../core/application/application.html | 36 +- .../api/jumpscale/core/application/index.html | 32 +- docs/api/jumpscale/core/base/events.html | 48 +- docs/api/jumpscale/core/base/factory.html | 213 +++-- docs/api/jumpscale/core/base/fields.html | 668 +++++++++------- docs/api/jumpscale/core/base/index.html | 34 +- docs/api/jumpscale/core/base/meta.html | 120 +-- .../jumpscale/core/base/store/filesystem.html | 104 ++- docs/api/jumpscale/core/base/store/index.html | 206 +++-- docs/api/jumpscale/core/base/store/redis.html | 95 ++- .../core/base/store/serializers.html | 62 +- .../jumpscale/core/base/store/whooshfts.html | 98 ++- docs/api/jumpscale/core/config/config.html | 146 ++-- docs/api/jumpscale/core/config/index.html | 26 +- docs/api/jumpscale/core/db/index.html | 30 +- docs/api/jumpscale/core/dirs/dirs.html | 103 +-- docs/api/jumpscale/core/dirs/index.html | 32 +- docs/api/jumpscale/core/events/index.html | 86 +- .../jumpscale/core/exceptions/exceptions.html | 136 ++-- docs/api/jumpscale/core/exceptions/index.html | 26 +- .../core/executors/command_builder.html | 40 +- docs/api/jumpscale/core/executors/index.html | 32 +- docs/api/jumpscale/core/executors/local.html | 40 +- docs/api/jumpscale/core/executors/remote.html | 68 +- docs/api/jumpscale/core/executors/tmux.html | 32 +- docs/api/jumpscale/core/index.html | 38 +- docs/api/jumpscale/core/logging/index.html | 32 +- docs/api/jumpscale/core/logging/logging.html | 235 +++--- docs/api/jumpscale/data/bcdb/bcdb.html | 305 +++---- docs/api/jumpscale/data/bcdb/clients.html | 127 +-- docs/api/jumpscale/data/bcdb/dumpsql.html | 30 +- docs/api/jumpscale/data/bcdb/flush.html | 30 +- docs/api/jumpscale/data/bcdb/index.html | 36 +- docs/api/jumpscale/data/bcdb/interfaces.html | 130 +-- docs/api/jumpscale/data/bcdb/models/base.html | 144 ++-- .../jumpscale/data/bcdb/models/db_model.html | 30 +- .../data/bcdb/models/emplyee_model.html | 30 +- .../api/jumpscale/data/bcdb/models/index.html | 46 +- .../data/bcdb/models/model_model.html | 30 +- .../data/bcdb/models/post_model.html | 30 +- .../data/bcdb/models/proj_model.html | 30 +- .../data/bcdb/models/test_model.html | 30 +- .../data/bcdb/models/user_model.html | 30 +- docs/api/jumpscale/data/cache/index.html | 24 +- .../jumpscale/data/encryption/exceptions.html | 30 +- docs/api/jumpscale/data/encryption/index.html | 30 +- .../jumpscale/data/encryption/mnemonic.html | 94 ++- .../jumpscale/data/encryption/wordlist.html | 24 +- docs/api/jumpscale/data/fake/index.html | 30 +- docs/api/jumpscale/data/hash/hash.html | 365 +++++---- docs/api/jumpscale/data/hash/index.html | 26 +- .../data/idgenerator/idgenerator.html | 138 ++-- .../api/jumpscale/data/idgenerator/index.html | 26 +- docs/api/jumpscale/data/index.html | 56 +- docs/api/jumpscale/data/inifile/index.html | 26 +- docs/api/jumpscale/data/inifile/inifile.html | 158 ++-- docs/api/jumpscale/data/nacl/index.html | 32 +- docs/api/jumpscale/data/nacl/jsnacl.html | 167 ++-- docs/api/jumpscale/data/platform/index.html | 148 ++-- .../jumpscale/data/random_names/index.html | 38 +- docs/api/jumpscale/data/schema/index.html | 26 +- docs/api/jumpscale/data/schema/schema.html | 90 ++- .../jumpscale/data/serializers/base64.html | 44 +- docs/api/jumpscale/data/serializers/dill.html | 24 +- .../api/jumpscale/data/serializers/index.html | 60 +- docs/api/jumpscale/data/serializers/json.html | 64 +- docs/api/jumpscale/data/serializers/lzma.html | 60 +- .../jumpscale/data/serializers/msgpack.html | 72 +- .../jumpscale/data/serializers/pickle.html | 72 +- docs/api/jumpscale/data/serializers/toml.html | 72 +- docs/api/jumpscale/data/serializers/yaml.html | 72 +- docs/api/jumpscale/data/tarfile/index.html | 26 +- docs/api/jumpscale/data/tarfile/tar_file.html | 74 +- .../jumpscale/data/terminaltable/index.html | 46 +- docs/api/jumpscale/data/text/index.html | 58 +- docs/api/jumpscale/data/time/index.html | 129 ++- docs/api/jumpscale/data/types/index.html | 28 +- docs/api/jumpscale/data/types/pritypes.html | 250 +++--- docs/api/jumpscale/data/types/types.html | 380 +++++---- docs/api/jumpscale/entry_points/index.html | 30 +- docs/api/jumpscale/entry_points/jsctl.html | 66 +- docs/api/jumpscale/entry_points/jsng.html | 24 +- docs/api/jumpscale/entry_points/jsync.html | 24 +- .../api/jumpscale/entry_points/usershell.html | 118 ++- docs/api/jumpscale/index.html | 40 +- docs/api/jumpscale/loader.html | 143 ++-- docs/api/jumpscale/sals/fs/index.html | 756 +++++++++++------- docs/api/jumpscale/sals/hostsfile/index.html | 85 +- docs/api/jumpscale/sals/index.html | 34 +- docs/api/jumpscale/sals/nettools/index.html | 302 ++++--- docs/api/jumpscale/sals/process/index.html | 522 +++++++----- docs/api/jumpscale/sals/testdocs/index.html | 46 +- docs/api/jumpscale/sals/unix/index.html | 26 +- docs/api/jumpscale/sals/unix/user.html | 68 +- docs/api/jumpscale/servers/index.html | 24 +- .../jumpscale/servers/openresty/index.html | 36 +- .../jumpscale/servers/openresty/location.html | 206 +++-- .../jumpscale/servers/openresty/server.html | 234 +++--- .../jumpscale/servers/openresty/utils.html | 30 +- docs/api/jumpscale/servers/rack/index.html | 32 +- docs/api/jumpscale/servers/rack/rack.html | 78 +- docs/api/jumpscale/shell/config.html | 62 +- docs/api/jumpscale/shell/index.html | 26 +- docs/api/jumpscale/threesdk/container.html | 73 +- .../jumpscale/threesdk/identitymanager.html | 56 +- docs/api/jumpscale/threesdk/index.html | 107 +-- docs/api/jumpscale/threesdk/settings.html | 24 +- .../tools/alerthandler/alerthandler.html | 177 ++-- .../jumpscale/tools/alerthandler/index.html | 32 +- .../api/jumpscale/tools/codeloader/index.html | 40 +- docs/api/jumpscale/tools/console/index.html | 254 +++--- .../tools/depsresolver/depsresolver.html | 125 ++- .../jumpscale/tools/depsresolver/index.html | 32 +- .../tools/errorhandler/errorhandler.html | 74 +- .../jumpscale/tools/errorhandler/index.html | 32 +- docs/api/jumpscale/tools/git/index.html | 148 ++-- .../jumpscale/tools/highlighter/index.html | 66 +- docs/api/jumpscale/tools/http/index.html | 24 +- docs/api/jumpscale/tools/index.html | 52 +- docs/api/jumpscale/tools/jinja2/index.html | 80 +- docs/api/jumpscale/tools/keygen/index.html | 22 +- docs/api/jumpscale/tools/keygen/keygen.html | 24 +- .../api/jumpscale/tools/schemac/compiler.html | 54 +- docs/api/jumpscale/tools/schemac/index.html | 48 +- .../tools/schemac/plugins/crystal.html | 36 +- .../tools/schemac/plugins/index.html | 32 +- .../jumpscale/tools/schemac/plugins/jsng.html | 36 +- .../tools/schemac/plugins/plugin.html | 41 +- .../tools/schemac/plugins/utils.html | 30 +- .../api/jumpscale/tools/startupcmd/index.html | 32 +- .../tools/startupcmd/startupcmd.html | 216 +++-- docs/api/jumpscale/tools/syncer/index.html | 87 +- docs/api/jumpscale/tools/timer/index.html | 30 +- poetry.lock | 68 +- pyproject.toml | 4 +- 149 files changed, 7615 insertions(+), 5653 deletions(-) diff --git a/docs/api/jumpscale/clients/base.html b/docs/api/jumpscale/clients/base.html index 4e036edac..f250b48a0 100644 --- a/docs/api/jumpscale/clients/base.html +++ b/docs/api/jumpscale/clients/base.html @@ -3,15 +3,17 @@ - + jumpscale.clients.base API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.clients.base

-Source code + +Expand source code +
from jumpscale.core.base import Base
 
 
@@ -43,11 +47,11 @@ 

Classes

(parent_=None, instance_name_=None, **values)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -62,9 +66,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class Client(Base):
     pass
@@ -76,11 +82,11 @@

Ancestors

Subclasses

Inherited members

    @@ -118,9 +124,7 @@

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/docker/docker.html b/docs/api/jumpscale/clients/docker/docker.html index bd513efac..3fb29f038 100644 --- a/docs/api/jumpscale/clients/docker/docker.html +++ b/docs/api/jumpscale/clients/docker/docker.html @@ -3,19 +3,21 @@ - + jumpscale.clients.docker.docker API documentation - - - - - + + + + + + +
    @@ -39,7 +41,9 @@

    Module jumpscale.clients.docker.docker

    or container.exec_run("ls /tmp)

-Source code + +Expand source code +
"""This module used to manage your conatiners, create ,run, list, delete and exec commands on docker
 for example
 ```
@@ -368,11 +372,11 @@ 

Classes

(*args, **kwargs)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -387,9 +391,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class DockerClient(Client):
     base_url = fields.String()
 
@@ -692,16 +698,18 @@ 

Instance variables

var base_url
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -717,9 +725,11 @@ 

Returns

var client
-
+
-Source code + +Expand source code +
@property
 def client(self):
     if not self.__client:
@@ -734,10 +744,10 @@ 

Returns

Methods

-def create(self, name, image, command=None, environment=None, entrypoint=None, volumes=None, devices=None, detach=True, ports=None, privileged=False, auto_remove=False, hostname='js-ng') +def create(self, name, image, command=None, environment=None, entrypoint=None, volumes=None, devices=None, detach=True, ports=None, privileged=False, auto_remove=False, hostname='js-ng')
-

Creates a docker container without starting it

+

Creates a docker container without starting it

Args

name : str
@@ -770,11 +780,13 @@

Args

hostname (str) - hostname to be set on docker container default "js-ng"

Returns

-
container
+
container
container object
-
+
-Source code + +Expand source code +
def create(
     self,
     name,
@@ -841,7 +853,7 @@ 

Returns

def delete(self, container_id, force=False)
-

Deletes docker container

+

Deletes docker container

Args

container_id : str
@@ -851,11 +863,13 @@

Args

Returns

-
bool
+
bool
True container deleted
-
+
-Source code + +Expand source code +
def delete(self, container_id, force=False):
     """Deletes docker container
     Args:
@@ -871,10 +885,10 @@ 

Returns

-def exec(self, container_id, cmd, stdout=True, stderr=True, stdin=False, tty=False, privileged=False, user='', detach=False, stream=False, socket=False, environment=None, workdir=None, demux=True) +def exec(self, container_id, cmd, stdout=True, stderr=True, stdin=False, tty=False, privileged=False, user='', detach=False, stream=False, socket=False, environment=None, workdir=None, demux=True)
-

Executes command docker container

+

Executes command docker container

Args

container_id : str
@@ -895,22 +909,23 @@

Args

demux (bool) – Return stdout and stderr separately

Returns

-
A tuple of (exit_code, output)
-
 
-
exit_code
+
A tuple of (exit_code, output)
+
exit_code
(int):

Exit code for the executed command or None if either stream or socket is True.

-
output
+
output
(generator, bytes, or tuple): If stream=True, a generator yielding response chunks. If socket=True, a socket object for the connection. If demux=True, a tuple of two bytes: stdout and stderr. A bytestring containing response data otherwise.
-
+
-Source code + +Expand source code +
def exec(
     self,
     container_id,
@@ -978,9 +993,11 @@ 

Returns

def exists(self, container_id)
-
+
-Source code + +Expand source code +
def exists(self, container_id):
     try:
         self.client.containers.get(container_id)
@@ -993,7 +1010,7 @@ 

Returns

def get(self, container_id)
-

Runs docker container

+

Runs docker container

Args

container_id : str
@@ -1001,11 +1018,13 @@

Args

Returns

-
container
+
container
container object
-
+
-Source code + +Expand source code +
def get(self, container_id):
     """Runs docker container
     Args:
@@ -1022,7 +1041,7 @@ 

Returns

def kill(self, container_id, sig=None)
-

Kills docker container

+

Kills docker container

Args

container_id : str
@@ -1032,11 +1051,13 @@

Args

Returns

-
bool
+
bool
True container killed
-
+
-Source code + +Expand source code +
def kill(self, container_id, sig=None):
     """Kills docker container
     Args:
@@ -1055,7 +1076,7 @@ 

Returns

def list(self, all=False)
-

Returns list of docker containers created

+

Returns list of docker containers created

Args

all : bool
@@ -1063,11 +1084,13 @@

Args

Returns

-
list
+
list
list of containers
-
+
-Source code + +Expand source code +
def list(self, all=False):
     """Returns list of docker containers created
     Args:
@@ -1083,7 +1106,7 @@ 

Returns

def restart(self, container_id, timeout=10)
-

Kills docker container

+

Kills docker container

Args

container_id : str
@@ -1093,11 +1116,13 @@

Args

Returns

-
bool
+
bool
True if container restarted
-
+
-Source code + +Expand source code +
def restart(self, container_id, timeout=10):
     """Kills docker container
     Args:
@@ -1113,10 +1138,10 @@ 

Returns

-def run(self, name, image, command=None, environment=None, entrypoint=None, volumes=None, devices=None, detach=True, ports=None, privileged=False, auto_remove=False, hostname='js-ng') +def run(self, name, image, command=None, environment=None, entrypoint=None, volumes=None, devices=None, detach=True, ports=None, privileged=False, auto_remove=False, hostname='js-ng')
-

Runs docker container

+

Runs docker container

Args

name : str
@@ -1149,11 +1174,13 @@

Args

hostname (str) - hostname to be set on docker container default "js-ng"

Returns

-
container
+
container
container object
-
+
-Source code + +Expand source code +
def run(
     self,
     name,
@@ -1219,7 +1246,7 @@ 

Returns

def start(self, container_id)
-

starts docker container

+

starts docker container

Args

container_id : str
@@ -1229,11 +1256,13 @@

Args

Returns

-
bool
+
bool
True if container started
-
+
-Source code + +Expand source code +
def start(self, container_id):
     """starts docker container
     Args:
@@ -1252,7 +1281,7 @@ 

Returns

def stop(self, container_id)
-

stops docker container

+

stops docker container

Args

container_id : str
@@ -1262,11 +1291,13 @@

Args

Returns

-
bool
+
bool
True if container started
-
+
-Source code + +Expand source code +
def stop(self, container_id):
     """stops docker container
     Args:
@@ -1333,9 +1364,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/docker/index.html b/docs/api/jumpscale/clients/docker/index.html index 7cdf9f682..c49e3fc2c 100644 --- a/docs/api/jumpscale/clients/docker/index.html +++ b/docs/api/jumpscale/clients/docker/index.html @@ -3,15 +3,17 @@ - + jumpscale.clients.docker API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.clients.docker

-Source code + +Expand source code +
def export_module_as():
 
     from jumpscale.core.base import StoredFactory
@@ -36,11 +40,11 @@ 

Sub-modules

jumpscale.clients.docker.docker
-

This module used to manage your conatiners, create ,run, list, delete and exec commands on docker +

This module used to manage your conatiners, create ,run, list, delete and exec commands on docker for example ``` getting docker clinet -cl = …

+cl = …

@@ -53,9 +57,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
 
     from jumpscale.core.base import StoredFactory
@@ -95,9 +101,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/git/git.html b/docs/api/jumpscale/clients/git/git.html index 71ef13ca7..a84c1bed1 100644 --- a/docs/api/jumpscale/clients/git/git.html +++ b/docs/api/jumpscale/clients/git/git.html @@ -3,15 +3,17 @@ - + jumpscale.clients.git.git API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.clients.git.git

-Source code + +Expand source code +
import git
 from jumpscale.clients.base import Client
 from jumpscale.core.base import fields
@@ -112,11 +116,11 @@ 

Classes

(*args, **kwargs)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -131,9 +135,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class GitClient(Client):
     path = fields.String()
 
@@ -213,9 +219,11 @@ 

Instance variables

var branch_name
-
+
-Source code + +Expand source code +
@property
 def branch_name(self):
     return self.repo.active_branch.name
@@ -223,16 +231,18 @@

Instance variables

var path
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -248,9 +258,11 @@ 

Returns

var remote_url
-
+
-Source code + +Expand source code +
@property
 def remote_url(self):
     return self.repo.remote().url
@@ -258,9 +270,11 @@

Returns

var repo
-
+
-Source code + +Expand source code +
@property
 def repo(self):
     if not self.__repo:
@@ -275,7 +289,7 @@ 

Methods

def commit(self, message, add_all=True)
-

adds a commit

+

adds a commit

Args

message : str
@@ -283,9 +297,15 @@

Args

add_all : bool, optional
will add all changes before commiting. Defaults to True.
-

Returns

+

Returns

+
+
[type]
+
[description]
+
-Source code + +Expand source code +
def commit(self, message, add_all=True):
     """adds a commit
 
@@ -306,14 +326,16 @@ 

Returns

def get_modified_files(self)
-

returns local changes in the repo

+

returns local changes in the repo

Returns

-
dict
+
dict
dict containing different types of changes(check git status man)
-
+
-Source code + +Expand source code +
def get_modified_files(self):
     """returns local changes in the repo
 
@@ -338,14 +360,16 @@ 

Returns

def pull(self)
-

Pulls from origin

+

Pulls from origin

Raises

-
j.exceptions.Input: if there is locaal changes
-
 
-
+
j.exceptions.Input
+
if there is locaal changes
+
-Source code + +Expand source code +
def pull(self):
     """Pulls from origin
 
@@ -358,12 +382,14 @@ 

Raises

-def set_remote_url(self, url, remote_name='origin') +def set_remote_url(self, url, remote_name='origin')
-
+
-Source code + +Expand source code +
def set_remote_url(self, url, remote_name="origin"):
     remote = self.repo.remote(remote_name)
     remote.set_url(url)
@@ -416,9 +442,7 @@

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/git/index.html b/docs/api/jumpscale/clients/git/index.html index aa5a876f8..1a9fbd71a 100644 --- a/docs/api/jumpscale/clients/git/index.html +++ b/docs/api/jumpscale/clients/git/index.html @@ -3,15 +3,17 @@ - + jumpscale.clients.git API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.clients.git

-Source code + +Expand source code +
def export_module_as():
 
     from jumpscale.core.base import StoredFactory
@@ -36,7 +40,7 @@ 

Sub-modules

jumpscale.clients.git.git
-
+
@@ -49,9 +53,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
 
     from jumpscale.core.base import StoredFactory
@@ -91,9 +97,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/index.html b/docs/api/jumpscale/clients/index.html index b802f2152..deeba2ba2 100644 --- a/docs/api/jumpscale/clients/index.html +++ b/docs/api/jumpscale/clients/index.html @@ -3,15 +3,17 @@ - + jumpscale.clients API documentation - - - - - + + + + + + +
@@ -26,31 +28,31 @@

Sub-modules

jumpscale.clients.base
-
+
jumpscale.clients.docker
-
+
jumpscale.clients.git
-
+
jumpscale.clients.redis
-
+
jumpscale.clients.sshclient
-
+
jumpscale.clients.sshkey
-
+
jumpscale.clients.zdb
-
+

@@ -87,9 +89,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/redis/index.html b/docs/api/jumpscale/clients/redis/index.html index 3317d75ce..ae61da0f8 100644 --- a/docs/api/jumpscale/clients/redis/index.html +++ b/docs/api/jumpscale/clients/redis/index.html @@ -3,15 +3,17 @@ - + jumpscale.clients.redis API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.clients.redis

-Source code + +Expand source code +
def export_module_as():
     from jumpscale.core.base import StoredFactory
 
@@ -35,7 +39,7 @@ 

Sub-modules

jumpscale.clients.redis.redis
-

Redis client

+

Redis client

@@ -48,9 +52,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
     from jumpscale.core.base import StoredFactory
 
@@ -89,9 +95,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/redis/redis.html b/docs/api/jumpscale/clients/redis/redis.html index 74c19b883..58979f0ef 100644 --- a/docs/api/jumpscale/clients/redis/redis.html +++ b/docs/api/jumpscale/clients/redis/redis.html @@ -3,15 +3,17 @@ - + jumpscale.clients.redis.redis API documentation - - - - - + + + + + + +
@@ -22,7 +24,9 @@

Module jumpscale.clients.redis.redis

Redis client

-Source code + +Expand source code +
"""
 Redis client
 """
@@ -70,7 +74,7 @@ 

Module jumpscale.clients.redis.redis

self.__client = Redis(self.hostname, self.port) return self.__client - + def is_running(self): try: return self.redis_client.ping() @@ -96,11 +100,11 @@

Classes

(*args, **kwargs)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -115,9 +119,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class RedisClient(Client):
     hostname = fields.String(default="localhost")
     port = fields.Integer(default=6379)
@@ -148,7 +154,7 @@ 

Args

self.__client = Redis(self.hostname, self.port) return self.__client - + def is_running(self): try: return self.redis_client.ping() @@ -169,16 +175,18 @@

Instance variables

var hostname
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -194,16 +202,18 @@ 

Returns

var password
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -219,16 +229,18 @@ 

Returns

var port
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -244,9 +256,11 @@ 

Returns

var redis_client
-
+
-Source code + +Expand source code +
@property
 def redis_client(self):
     if not self.__client:
@@ -265,9 +279,11 @@ 

Methods

def is_running(self)
-
+
-Source code + +Expand source code +
def is_running(self):
     try:
         return self.redis_client.ping()
@@ -292,9 +308,11 @@ 

Inherited members

(instance, name, new_value)
-
+
-Source code + +Expand source code +
class RedisClientAttributeUpdated(AttributeUpdateEvent):
     pass
@@ -339,9 +357,7 @@

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/sshclient/index.html b/docs/api/jumpscale/clients/sshclient/index.html index 27f9a8e15..901137305 100644 --- a/docs/api/jumpscale/clients/sshclient/index.html +++ b/docs/api/jumpscale/clients/sshclient/index.html @@ -3,15 +3,17 @@ - + jumpscale.clients.sshclient API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.clients.sshclient

-Source code + +Expand source code +
def export_module_as():
 
     from jumpscale.core.base import StoredFactory
@@ -36,7 +40,7 @@ 

Sub-modules

jumpscale.clients.sshclient.sshclient
-

SSHClient modules helps connecting and executing commands to a remote machine …

+

SSHClient modules helps connecting and executing commands to a remote machine …

@@ -49,9 +53,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
 
     from jumpscale.core.base import StoredFactory
@@ -91,9 +97,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/sshclient/sshclient.html b/docs/api/jumpscale/clients/sshclient/sshclient.html index ca9eb8edb..39b083386 100644 --- a/docs/api/jumpscale/clients/sshclient/sshclient.html +++ b/docs/api/jumpscale/clients/sshclient/sshclient.html @@ -3,15 +3,17 @@ - + jumpscale.clients.sshclient.sshclient API documentation - - - - - + + + + + + +
@@ -22,7 +24,7 @@

Module jumpscale.clients.sshclient.sshclient

SSHClient modules helps connecting and executing commands to a remote machine.

Create SSH Key

-
JS-NG> xmonader = j.clients.sshkey.new("xmonader")
+
JS-NG> xmonader = j.clients.sshkey.new("xmonader")
 JS-NG> xmonader.private_key_path = "/home/xmonader/.ssh/id_rsa"
 JS-NG>
 
@@ -38,7 +40,9 @@ 

Creating sshcl

-Source code + +Expand source code +
"""
 
 SSHClient modules helps connecting and executing commands to a remote machine.
@@ -157,7 +161,7 @@ 

Classes

(*args, **kwargs)
-

SSHClient has the following properties: +

SSHClient has the following properties: sshkey (str): sshkey to use within that client host (str): host ip user (str): user to connect as default: True @@ -167,7 +171,7 @@

Classes

connect_timeout (int): timeout (default 10 seconds)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -182,9 +186,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class SSHClient(Client):
     """
     SSHClient has the following properties:
@@ -267,16 +273,18 @@ 

Instance variables

var connect_timeout
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -292,16 +300,18 @@ 

Returns

var connection_kwargs
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -317,16 +327,18 @@ 

Returns

var forward_agent
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -342,16 +354,18 @@ 

Returns

var host
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -367,16 +381,18 @@ 

Returns

var inline_ssh_env
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -392,16 +408,18 @@ 

Returns

var port
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -417,9 +435,11 @@ 

Returns

var sshclient
-
+
-Source code + +Expand source code +
@property
 def sshclient(self):
     self.validate()
@@ -443,16 +463,18 @@ 

Returns

var sshkey
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -468,16 +490,18 @@ 

Returns

var user
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -498,12 +522,14 @@ 

Methods

def reset_connection(self)
-

Reset the connection +

Reset the connection e.g localconnection = j.clients.sshclient.new("localconnection") -localconnection.reset_connection()

+localconnection.reset_connection()

-Source code + +Expand source code +
def reset_connection(self):
     """ Reset the connection
     e.g
@@ -566,9 +592,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/sshkey/index.html b/docs/api/jumpscale/clients/sshkey/index.html index 580a72ec2..e6f247a79 100644 --- a/docs/api/jumpscale/clients/sshkey/index.html +++ b/docs/api/jumpscale/clients/sshkey/index.html @@ -3,15 +3,17 @@ - + jumpscale.clients.sshkey API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.clients.sshkey

-Source code + +Expand source code +
def export_module_as():
     from jumpscale.core.base import StoredFactory
 
@@ -35,8 +39,8 @@ 

Sub-modules

jumpscale.clients.sshkey.sshkey
-

This module used to manage your ssh keys, get the public key, get the private key, generate key, write key to the file system, -delete key from the …

+

This module used to manage your ssh keys, get the public key, get the private key, generate key, write key to the file system, +delete key from the …

@@ -49,9 +53,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
     from jumpscale.core.base import StoredFactory
 
@@ -90,9 +96,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/sshkey/sshkey.html b/docs/api/jumpscale/clients/sshkey/sshkey.html index 6268aeb60..44ccdacfe 100644 --- a/docs/api/jumpscale/clients/sshkey/sshkey.html +++ b/docs/api/jumpscale/clients/sshkey/sshkey.html @@ -3,16 +3,18 @@ - + jumpscale.clients.sshkey.sshkey API documentation - - - - - + + + + + + +
@@ -39,7 +41,9 @@

Writing ssh keys to files system

ssh_cl.write_to_filesystem()
 
-Source code + +Expand source code +
"""
 This module used to manage your ssh keys, get the public key, get the private key, generate key, write key to the file system,
 delete key from the file system, load key from the file system.
@@ -161,11 +165,11 @@ 

Classes

(*args, **kwargs)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -180,9 +184,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class SSHKeyClient(Client):
     public_key = fields.String()
     private_key = fields.Secret()
@@ -267,16 +273,18 @@ 

Instance variables

var allow_agent
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -292,16 +300,18 @@ 

Returns

var duration
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -317,16 +327,18 @@ 

Returns

var passphrase
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -342,16 +354,18 @@ 

Returns

var private_key
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -367,16 +381,18 @@ 

Returns

var private_key_path
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -392,16 +408,18 @@ 

Returns

var public_key
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -417,15 +435,17 @@ 

Returns

var public_key_path
-

Get the public key path +

Get the public key path e.g ssh_cl = j.clients.sshkey.get("ssh_test") ssh_cl.public_key_path -> "/root/.config/jumpscale/sshkeys/tU59lc6P.pub" Returns -str: the path for public key

+str: the path for public key

-Source code + +Expand source code +
@property
 def public_key_path(self):
     """ Get the public key path
@@ -446,9 +466,11 @@ 

Methods

def delete_from_filesystem(self)
-
+
-Source code + +Expand source code +
def delete_from_filesystem(self):
     pass
@@ -457,12 +479,14 @@

Methods

def generate_keys(self)
-

Generate a new ssh key +

Generate a new ssh key e.g ssh_cl = j.clients.sshkey.get("ssh_test") -ssh_cl.generate_keys()

+ssh_cl.generate_keys()

-Source code + +Expand source code +
def generate_keys(self):
     """Generate a new ssh key
     e.g
@@ -489,12 +513,14 @@ 

Methods

def load_from_file_system(self)
-

Load public key and private key from files using private key path and public key path +

Load public key and private key from files using private key path and public key path e.g ssh_cl = j.clients.sshkey.get("ssh_test") -ssh_cl.load_from_file_system()

+ssh_cl.load_from_file_system()

-Source code + +Expand source code +
def load_from_file_system(self):
     """ Load public key and private key from files using private key path and public key path
     e.g
@@ -509,12 +535,14 @@ 

Methods

def write_to_filesystem(self)
-

Write public key and private key to files using private key path and public key path. +

Write public key and private key to files using private key path and public key path. e.g ssh_cl = j.clients.sshkey.get("ssh_test") -ssh_cl.write_to_filesystem()

+ssh_cl.write_to_filesystem()

-Source code + +Expand source code +
def write_to_filesystem(self):
     """ Write public key and private key to files using private key path and public key path.
     e.g
@@ -589,9 +617,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/zdb/client.html b/docs/api/jumpscale/clients/zdb/client.html index c85eb364c..9b5608256 100644 --- a/docs/api/jumpscale/clients/zdb/client.html +++ b/docs/api/jumpscale/clients/zdb/client.html @@ -3,15 +3,17 @@ - + jumpscale.clients.zdb.client API documentation - - - - - + + + + + + +
@@ -24,7 +26,9 @@

To get a new ZDB instance

zcl = j.clients.zdb.get("instance", admin=True, mode="user")

This will gets an instance with admin capabilities in user mode

-Source code + +Expand source code +
"""
 # To get a new ZDB instance
 
@@ -407,12 +411,14 @@ 

Classes

class Mode -(*args, **kwargs) +(value, names=None, *, module=None, qualname=None, type=None, start=1)
-

An enumeration.

+

An enumeration.

-Source code + +Expand source code +
class Mode(Enum):
     SEQ = "seq"
     USER = "user"
@@ -425,11 +431,11 @@

Class variables

var SEQ
-
+
var USER
-
+
@@ -438,11 +444,11 @@

Class variables

(*args, **kwargs)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -457,9 +463,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-

+
-Source code + +Expand source code +
class ZDBAdminClient(ZDBClient):
     def auth(self):
         assert self.admin
@@ -558,9 +566,11 @@ 

Methods

def auth(self)
-
+
-Source code + +Expand source code +
def auth(self):
     assert self.admin
     if self.secret_:
@@ -572,9 +582,11 @@ 

Methods

def namespace_delete(self, name)
-
+
-Source code + +Expand source code +
def namespace_delete(self, name):
     assert self.admin
     self.auth()
@@ -586,9 +598,11 @@ 

Methods

def namespace_exists(self, name)
-
+
-Source code + +Expand source code +
def namespace_exists(self, name):
     assert self.admin
     self.auth()
@@ -602,12 +616,14 @@ 

Methods

-def namespace_get(self, name, secret='') +def namespace_get(self, name, secret='')
-
+
-Source code + +Expand source code +
def namespace_get(self, name, secret=""):
     assert self.admin
     self.auth()
@@ -618,14 +634,16 @@ 

Methods

def namespace_new(self, name, secret=None, maxsize=0, die=False)
-

check namespace exists & will return zdb client to that namespace

+

check namespace exists & will return zdb client to that namespace

:param name: :param secret: :param maxsize: :param die: -:return:

+:return:

-Source code + +Expand source code +
def namespace_new(self, name, secret=None, maxsize=0, die=False):
     """
     check namespace exists & will return zdb client to that namespace
@@ -669,9 +687,11 @@ 

Methods

def namespaces_list(self)
-
+
-Source code + +Expand source code +
def namespaces_list(self):
     assert self.admin
     self.auth()
@@ -683,11 +703,13 @@ 

Methods

def reset(self, ignore=[])
-

dangerous, will remove all namespaces & all data +

dangerous, will remove all namespaces & all data :param: list of namespace names not to reset -:return:

+:return:

-Source code + +Expand source code +
def reset(self, ignore=[]):
     """
     dangerous, will remove all namespaces & all data
@@ -730,11 +752,11 @@ 

Inherited members

(*args, **kwargs)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -749,9 +771,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class ZDBClient(Client):
     addr = fields.String(default="localhost")
     port = fields.Integer(default=9900)
@@ -954,16 +978,18 @@ 

Instance variables

var addr
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -979,16 +1005,18 @@ 

Returns

var admin
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -1004,10 +1032,12 @@ 

Returns

var count
-

:return: return the number of entries in the namespace -:rtype: int

+

:return: return the number of entries in the namespace +:rtype: int

-Source code + +Expand source code +
@property
 def count(self):
     """
@@ -1019,16 +1049,18 @@ 

Returns

var mode
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -1044,10 +1076,12 @@ 

Returns

var next_id
-

:return: return the next id -:rtype: int

+

:return: return the next id +:rtype: int

-Source code + +Expand source code +
@property
 def next_id(self):
     """
@@ -1060,9 +1094,11 @@ 

Returns

var nsinfo
-
+
-Source code + +Expand source code +
@property
 def nsinfo(self):
     cmd = self.redis.execute_command("NSINFO", self.nsname)
@@ -1071,16 +1107,18 @@ 

Returns

var nsname
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -1096,16 +1134,18 @@ 

Returns

var port
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -1121,9 +1161,11 @@ 

Returns

var redis
-
+
-Source code + +Expand source code +
@property
 def redis(self):
     if not self._redis:
@@ -1142,16 +1184,18 @@ 

Returns

var secret_
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -1172,9 +1216,11 @@ 

Methods

def delete(self, key)
-
+
-Source code + +Expand source code +
def delete(self, key):
     if not key:
         raise j.exceptions.Value("key must be provided")
@@ -1186,9 +1232,11 @@ 

Methods

def exists(self, key)
-
+
-Source code + +Expand source code +
def exists(self, key):
     key = self._key_encode(key)
     return self.redis.execute_command("EXISTS", key) == 1
@@ -1198,12 +1246,14 @@

Methods

def flush(self)
-

will remove all data from the database DANGEROUS !!!! +

will remove all data from the database DANGEROUS !!!! This is only allowed on private and password protected namespace You need to select the namespace before running the command. -:return:

+:return:

-Source code + +Expand source code +
def flush(self):
     """
     will remove all data from the database DANGEROUS !!!!
@@ -1219,9 +1269,11 @@ 

Methods

def get(self, key)
-
+
-Source code + +Expand source code +
def get(self, key):
     key = self._key_encode(key)
     return self.redis.execute_command("GET", key)
@@ -1231,7 +1283,7 @@

Methods

def iterate(self, key_start=None, reverse=False, keyonly=False)
-

walk over all the namespace and yield (key,data) for each entries in a namespace

+

walk over all the namespace and yield (key,data) for each entries in a namespace

:param key_start: if specified start to walk from that key instead of the first one, defaults to None :param key_start: str, optional :param reverse: decide how to walk the namespace @@ -1241,9 +1293,11 @@

Methods

:param reverse: bool, optional :param keyonly: [description], defaults to False :param keyonly: bool, optional -:raises e: [description]

+:raises e: [description]

-Source code + +Expand source code +
def iterate(self, key_start=None, reverse=False, keyonly=False):
     """
     walk over all the namespace and yield (key,data) for each entries in a namespace
@@ -1298,7 +1352,7 @@ 

Methods

def list(self, key_start=None, reverse=False)
-

list all the keys in the namespace

+

list all the keys in the namespace

:param key_start: if specified start to walk from that key instead of the first one, defaults to None :param key_start: str, optional :param reverse: decide how to walk the namespace @@ -1307,9 +1361,11 @@

Methods

defaults to False :param reverse: bool, optional :return: list of keys -:rtype: [str]

+:rtype: [str]

-Source code + +Expand source code +
def list(self, key_start=None, reverse=False):
     """
     list all the keys in the namespace
@@ -1334,10 +1390,12 @@ 

Methods

def ping(self)
-

go to default namespace & ping -:return:

+

go to default namespace & ping +:return:

-Source code + +Expand source code +
def ping(self):
     """
     go to default namespace & ping
@@ -1350,9 +1408,11 @@ 

Methods

def set(self, data, key=None)
-
+
-Source code + +Expand source code +
def set(self, data, key=None):
     key = key or ""
     key = self._key_encode(key)
@@ -1366,9 +1426,11 @@ 

Methods

def stop(self)
-
+
-Source code + +Expand source code +
def stop(self):
     pass
@@ -1390,10 +1452,12 @@

Inherited members

(namespace=None, namespace_password=None, admin=False, *args, **kwargs)
-

ZDBConnection implement the custom selection of namespace -on 0-DB

+

ZDBConnection implement the custom selection of namespace +on 0-DB

-Source code + +Expand source code +
class ZDBConnection(redis.Connection):
     """
     ZDBConnection implement the custom selection of namespace
@@ -1440,9 +1504,11 @@ 

Methods

def on_connect(self)
-

when a new connection is created, switch to the proper namespace

+

when a new connection is created, switch to the proper namespace

-Source code + +Expand source code +
def on_connect(self):
     """
     when a new connection is created, switch to the proper namespace
@@ -1544,9 +1610,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/clients/zdb/index.html b/docs/api/jumpscale/clients/zdb/index.html index 26492b3dc..9d5157acf 100644 --- a/docs/api/jumpscale/clients/zdb/index.html +++ b/docs/api/jumpscale/clients/zdb/index.html @@ -3,15 +3,17 @@ - + jumpscale.clients.zdb API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.clients.zdb

-Source code + +Expand source code +
from jumpscale.core.base import StoredFactory
 from .client import ZDBClient, ZDBAdminClient
 
@@ -43,7 +47,7 @@ 

Sub-modules

jumpscale.clients.zdb.client
-

To get a new ZDB instance …

+

To get a new ZDB instance …

@@ -56,9 +60,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
 
     return ZDBFactory(ZDBClient)
@@ -74,7 +80,7 @@

Classes

(type_, name_=None, parent_instance_=None, parent_factory_=None)
-

Stored factories are a custom type of Factory, which uses current configured store backend +

Stored factories are a custom type of Factory, which uses current configured store backend to store all instance configurations.

get a new stored factory given the type to create and store instances for.

Any factory can have a name, parent Base instance and a parent factory.

@@ -89,9 +95,11 @@

Args

a parent Base instance. Defaults to None.
parent_factory_ : Factory, optional
a parent Factory. Defaults to None.
-
+
-Source code + +Expand source code +
class ZDBFactory(StoredFactory):
     def new(self, name, *args, **kwargs):
         if kwargs.get("admin"):
@@ -157,9 +165,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/application/application.html b/docs/api/jumpscale/core/application/application.html index 592ba5200..75c0f7473 100644 --- a/docs/api/jumpscale/core/application/application.html +++ b/docs/api/jumpscale/core/application/application.html @@ -3,15 +3,17 @@ - + jumpscale.core.application.application API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.core.application.application

-Source code + +Expand source code +
import sys
 
 from jumpscale.loader import j
@@ -49,9 +53,11 @@ 

Classes

class Application
-
+
-Source code + +Expand source code +
class Application:
     def __init__(self):
         sys.excepthook = j.tools.errorhandler.excepthook
@@ -64,9 +70,11 @@ 

Instance variables

var process_id
-
+
-Source code + +Expand source code +
@property
 def process_id(self):
     return j.sals.process.get_my_process().pid
@@ -102,9 +110,7 @@

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/application/index.html b/docs/api/jumpscale/core/application/index.html index c06302d40..acf7b8a81 100644 --- a/docs/api/jumpscale/core/application/index.html +++ b/docs/api/jumpscale/core/application/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.application API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.core.application

-Source code + +Expand source code +
def export_module_as():
     from .application import Application
 
@@ -33,7 +37,7 @@ 

Sub-modules

jumpscale.core.application.application
-
+
@@ -46,9 +50,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
     from .application import Application
 
@@ -85,9 +91,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/events.html b/docs/api/jumpscale/core/base/events.html index da5116554..61c00bf88 100644 --- a/docs/api/jumpscale/core/base/events.html +++ b/docs/api/jumpscale/core/base/events.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.events API documentation - - - - - + + + + + + +
@@ -22,7 +24,9 @@

Module jumpscale.core.base.events

Base and factory related events.

-Source code + +Expand source code +
"""
 Base and factory related events.
 """
@@ -65,9 +69,11 @@ 

Classes

(instance, name, new_value)
-
+
-Source code + +Expand source code +
class AttributeUpdateEvent(InstanceEvent):
     def __init__(self, instance, name, new_value):
         super().__init__(instance=instance)
@@ -88,9 +94,11 @@ 

Subclasses

(instance=None, factory=None)
-
+
-Source code + +Expand source code +
class InstanceCreateEvent(InstanceEvent):
     pass
@@ -104,9 +112,11 @@

Ancestors

(name, instance=None, factory=None)
-
+
-Source code + +Expand source code +
class InstanceDeleteEvent(InstanceEvent):
     def __init__(self, name, instance=None, factory=None):
         super().__init__(instance, factory)
@@ -122,9 +132,11 @@ 

Ancestors

(instance=None, factory=None)
-
+
-Source code + +Expand source code +
class InstanceEvent:
     def __init__(self, instance=None, factory=None):
         self.instance = instance
@@ -171,9 +183,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/factory.html b/docs/api/jumpscale/core/base/factory.html index 644555135..de98da0b4 100644 --- a/docs/api/jumpscale/core/base/factory.html +++ b/docs/api/jumpscale/core/base/factory.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.factory API documentation - - - - - + + + + + + +
@@ -20,11 +22,11 @@

Module jumpscale.core.base.factory

-

Hierarchal configurations and factories for any Base type/class.

+

Hierarchal configurations and factories for any Base type/class.

It is implemented using:

    -
  • Factory: the base for all factories, implements an in-memory factory for any type.
  • -
  • StoredFactory: a factory which stores and load configuration according to the current configured store backend.
  • +
  • Factory: the base for all factories, implements an in-memory factory for any type.
  • +
  • StoredFactory: a factory which stores and load configuration according to the current configured store backend.

Default store configured is file system, you can show current store config by doing:

✗ jsctl config get store
@@ -42,7 +44,9 @@ 

Module jumpscale.core.base.factory

This will use current redis config (hostname: localhost, port: 6379).

-Source code + +Expand source code +
"""
 Hierarchal configurations and factories for any `jumpscale.core.base.meta.Base` type/class.
 
@@ -648,9 +652,11 @@ 

Classes

(*args, **kwargs)
-

raised when you try to create an instance by the same name of an existing one for a factory

+

raised when you try to create an instance by the same name of an existing one for a factory

-Source code + +Expand source code +
class DuplicateError(Exception):
     """
     raised when you try to create an instance by the same name of an existing one for a factory
@@ -667,10 +673,10 @@ 

Ancestors

(type_, name_=None, parent_instance_=None, parent_factory_=None)
-

Base factory, where you can create/get/list/delete new instances.

+

Base factory, where you can create/get/list/delete new instances.

All of the operations are done in memory, also instances will be available as properties.

Example:

-

+

 class Car(Base):
     name = fields.String()
     color = fields.String()
@@ -691,11 +697,13 @@ 

Args

factory name. Defaults to None.
parent_instance_ : Base, optional
a parent Base instance. Defaults to None.
-
parent_factory_ : Factory, optional
-
a parent Factory. Defaults to None.
-

+
parent_factory_ : Factory, optional
+
a parent Factory. Defaults to None.
+
-Source code + +Expand source code +
class Factory:
     """
     Base factory, where you can create/get/list/delete new instances.
@@ -921,9 +929,11 @@ 

Instance variables

var name
-
+
-Source code + +Expand source code +
@property
 def name(self):
     return self.__name
@@ -931,9 +941,11 @@

Instance variables

var parent_factory
-
+
-Source code + +Expand source code +
@property
 def parent_factory(self):
     return self.__parent_factory
@@ -941,9 +953,11 @@

Instance variables

var parent_instance
-
+
-Source code + +Expand source code +
@property
 def parent_instance(self):
     return self.__parent_instance
@@ -956,12 +970,14 @@

Methods

def delete(self, name)
-

delete an instance (with its attribute)

+

delete an instance (with its attribute)

this will update the count and trigger _deleted

Args

-

name (str)

+

name (str)

-Source code + +Expand source code +
def delete(self, name):
     """
     delete an instance (with its attribute)
@@ -980,7 +996,7 @@ 

Args

def find(self, name)
-

find an instance with the given name

+

find an instance with the given name

Args

name : str
@@ -988,16 +1004,18 @@

Args

Raises

-
ValueError
+
ValueError
in case the name is an internal attribute of this factory, like get or new.

Returns

-
Base or NoneType: an instance or none
-
 
-
+
Base or NoneType
+
an instance or none
+
-Source code + +Expand source code +
def find(self, name):
     """
     find an instance with the given name
@@ -1021,7 +1039,7 @@ 

Returns

def get(self, name, *args, **kwargs)
-

get an instance (will create if it does not exist)

+

get an instance (will create if it does not exist)

if the instance with name exists, args and kwargs are ignored.

Args

@@ -1034,16 +1052,18 @@

Args

Raises

-
DuplicateError
+
DuplicateError
in case an instance with the same name exists

Returns

-
Base
+
Base
instance
-
+
-Source code + +Expand source code +
def get(self, name, *args, **kwargs):
     """
     get an instance (will create if it does not exist)
@@ -1071,15 +1091,14 @@ 

Returns

def list_all(self)
-

get a set of all instance names

+

get a set of all instance names

this only get a list of all properties that are of factory Base type.

Returns

-
-
set
-
 
-
+

set

-Source code + +Expand source code +
def list_all(self):
     """
     get a set of all instance names
@@ -1100,7 +1119,7 @@ 

Returns

def new(self, name, *args, **kwargs)
-

get a new instance and make it available as an attribute

+

get a new instance and make it available as an attribute

Args

name : str
@@ -1112,16 +1131,18 @@

Args

Raises

-
DuplicateError
+
DuplicateError
in case an instance with the same name exists

Returns

-
Base
+
Base
instance
-
+
-Source code + +Expand source code +
def new(self, name, *args, **kwargs):
     """
     get a new instance and make it available as an attribute
@@ -1152,7 +1173,7 @@ 

Returns

(type_, name_=None, parent_instance_=None, parent_factory_=None)
-

Stored factories are a custom type of Factory, which uses current configured store backend +

Stored factories are a custom type of Factory, which uses current configured store backend to store all instance configurations.

get a new stored factory given the type to create and store instances for.

Any factory can have a name, parent Base instance and a parent factory.

@@ -1165,11 +1186,13 @@

Args

factory name. Defaults to None.
parent_instance_ : Base, optional
a parent Base instance. Defaults to None.
-
parent_factory_ : Factory, optional
-
a parent Factory. Defaults to None.
-
+
parent_factory_ : Factory, optional
+
a parent Factory. Defaults to None.
+
-Source code + +Expand source code +
class StoredFactory(events.Handler, Factory):
     """
     Stored factories are a custom type of `Factory`, which uses current configured store backend
@@ -1502,23 +1525,25 @@ 

Class variables

var STORE
-

Filesystem store is an EncryptedConfigStore

+

Filesystem store is an EncryptedConfigStore

It saves the config relative to config_env.get_store_config("filesystem")

-

To store every instance config in a different path, it uses the given Location.

+

To store every instance config in a different path, it uses the given Location.

Instance variables

var location
-

get a unique location for this factory

+

get a unique location for this factory

Returns

-
Location
+
Location
location object
-
+
-Source code + +Expand source code +
@property
 def location(self):
     """
@@ -1551,9 +1576,11 @@ 

Returns

var parent_location
-
+
-Source code + +Expand source code +
@property
 def parent_location(self):
     if not self.parent_factory:
@@ -1563,9 +1590,11 @@ 

Returns

var store
-
+
-Source code + +Expand source code +
@property
 def store(self):
     if not self.__store:
@@ -1580,7 +1609,7 @@ 

Methods

def find_many(self, cursor_=None, limit_=None, **query)
-

do a search against the store (not loaded objects) with this query.

+

do a search against the store (not loaded objects) with this query.

queries can relate to current store backend used.

Keyword Args: cursor_ (any, optional): an optional cursor, to start searching from. Defaults to None. @@ -1588,11 +1617,13 @@

Methods

query: a mapping for field/query, e.g. first_name="aa"

Returns

-
tuple
+
tuple
the new cursor, total results count, and a list of objects as a result
-
+
-Source code + +Expand source code +
def find_many(self, cursor_=None, limit_=None, **query):
     """
     do a search against the store (not loaded objects) with this query.
@@ -1618,7 +1649,7 @@ 

Returns

def get_instance_property(self, name)
-

return a new property descriptor for a given name, this will help in lazy-loading +

return a new property descriptor for a given name, this will help in lazy-loading as this property will only create and load an instance from the store once accessed.

Args

@@ -1627,11 +1658,13 @@

Args

Returns

-
property
+
property
property descriptor (object)
-
+
-Source code + +Expand source code +
def get_instance_property(self, name):
     """
     return a new property descriptor for a given name, this will help in lazy-loading
@@ -1662,14 +1695,16 @@ 

Returns

def handle(self, ev)
-

handle when the parent instance is deleted

+

handle when the parent instance is deleted

Args

ev : InstanceDeleteEvent
instance delete event
-
+
-Source code + +Expand source code +
def handle(self, ev):
     """
     handle when the parent instance is deleted
@@ -1692,14 +1727,16 @@ 

Args

def list_all(self)
-

get all instance names (stored or not)

+

get all instance names (stored or not)

Returns

-
set
+
set
instance names
-
+
-Source code + +Expand source code +
def list_all(self):
     """
     get all instance names (stored or not)
@@ -1715,7 +1752,7 @@ 

Returns

def new(self, name, *args, **kwargs)
-

get a new instance and make it available as an attribute

+

get a new instance and make it available as an attribute

this method also initialize sub-factories for this instance.

Args

@@ -1728,16 +1765,18 @@

Args

Raises

-
DuplicateError
+
DuplicateError
in case an instance with the same name exists

Returns

-
Base
+
Base
instance
-
+
-Source code + +Expand source code +
def new(self, name, *args, **kwargs):
     """
     get a new instance and make it available as an attribute
@@ -1824,9 +1863,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/fields.html b/docs/api/jumpscale/core/base/fields.html index b2db06e7e..afa489691 100644 --- a/docs/api/jumpscale/core/base/fields.html +++ b/docs/api/jumpscale/core/base/fields.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.fields API documentation - - - - - + + + + + + +
@@ -23,9 +25,9 @@

Module jumpscale.core.base.fields

fields have all possible fields where they can be used as a class variables for any Base classselfself.

Field instances will not hold the value or any date, they will be converted by Base to function descriptors, where the data itself resides in Base objects (instances).

-

See jumpscale.core.base.meta.

+

See jumpscale.core.base.meta.

Example:

-
from enum import Enum
+
from enum import Enum
 
 class Permission(Base):
     read_posts = fields.Boolean()
@@ -62,13 +64,15 @@ 

Module jumpscale.core.base.fields

In addition to custom options every field can accept and define, they can be used in the following methods:

  • validate: -raises a ValidationError in case it's not valid.
  • +raises a ValidationError in case it's not valid.
  • to_raw: returns a raw (primitive type) object from a value of this field
  • from_raw: returns a new object of the field type (if any) from a raw value

No need for from_raw to raise an error on e.g. type mismatch, as validate will do the validation.

-Source code + +Expand source code +
"""
 `fields` have all possible fields where they can be used as a class variables for any Base classselfself.
 
@@ -1239,7 +1243,7 @@ 

Classes

(default=False, **kwargs)
-

Boolean fields to hold a bool value.

+

Boolean fields to hold a bool value.

values can be set using strings or numbers and will be converted to bool like:

  • "on", "off"
  • @@ -1253,10 +1257,12 @@

    Args

    default : bool, optional
    default value. Defaults to False.
    kwargs
    -
    any keyword arguments supported by Field
    -
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Boolean(Typed):
     def __init__(self, default=False, **kwargs):
         """
@@ -1309,16 +1315,18 @@ 

Methods

def from_raw(self, value)
-

get bool value from strings and numbers

+

get bool value from strings and numbers

Args

value (str or int or float or complex)

Returns

-
bool
+
bool
boolean value
-
+
-Source code + +Expand source code +
def from_raw(self, value):
     """
     get bool value from strings and numbers
@@ -1359,17 +1367,19 @@ 

Inherited members

(default=b'', encoding='utf-8', **kwargs)
-

same as string field, but will hold bytes.

+

same as string field, but will hold bytes.

Args

default (b"", optional): default value. Defaults to b""
encoding
encoding to be used when serializing the value. Defaults to "utf-8"
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Bytes(Typed):
     def __init__(self, default=b"", encoding="utf-8", **kwargs):
         """
@@ -1414,7 +1424,7 @@ 

Inherited members

(default=None, format_=None, **kwargs)
-

date field, will hold datetime.date objects.

+

date field, will hold datetime.date objects.

values can be set using strings in the given format_ too like "12/1/2020" or a utc timestamp, they will converted to objects.

Args

@@ -1424,10 +1434,12 @@

Args

format_ : str, optional
date format. Defaults to "%Y-%m-%d" if None.
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Date(DateTimeMixin, Typed):
     def __init__(self, default=None, format_=None, **kwargs):
         """
@@ -1475,7 +1487,7 @@ 

Inherited members

(default=None, format_=None, **kwargs)
-

datetime field, will hold datetime.datetime objects.

+

datetime field, will hold datetime.datetime objects.

values can be set using strings in the given format_ too like "12/1/2020" or a utc timestamp, they will converted to objects.

Args

@@ -1485,10 +1497,12 @@

Args

format_ : str, optional
datetime format. Defaults to "%Y-%m-%d %H:%M" if None.
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class DateTime(DateTimeMixin, Typed):
     # maybe add something like auto_now and auto_today for date/time fields
     def __init__(self, default=None, format_=None, **kwargs):
@@ -1534,12 +1548,13 @@ 

Inherited members

class DateTimeMixin -(*args, **kwargs)
-
+
-Source code + +Expand source code +
class DateTimeMixin:
     def get_arrow_obj(self, value):
         """
@@ -1621,8 +1636,8 @@ 

Inherited members

Subclasses

Methods

@@ -1631,7 +1646,7 @@

Methods

def from_raw(self, value)
-

get a datetime object from a numberic (epoch) or string value

+

get a datetime object from a numberic (epoch) or string value

Args

value : str or int or float
@@ -1639,11 +1654,13 @@

Args

Returns

-
datetime.datetime or datetime.date or datetime.time: datetime or date/time object
-
 
-
+
datetime.datetime or datetime.date or datetime.time
+
datetime or date/time object
+
-Source code + +Expand source code +
def from_raw(self, value):
     """
     get a datetime object from a numberic (epoch) or string value
@@ -1674,7 +1691,7 @@ 

Returns

def get_arrow_obj(self, value)
-

get an arrow object from int, float and str and datetime.time objects.

+

get an arrow object from int, float and str and datetime.time objects.

Args

value : int or float or str
@@ -1682,11 +1699,13 @@

Args

Returns

-
arrow.Arrow: arrow object in utc
-
 
-
+
arrow.Arrow
+
arrow object in utc
+
-Source code + +Expand source code +
def get_arrow_obj(self, value):
     """
     get an arrow object from int, float and str and `datetime.time` objects.
@@ -1712,7 +1731,7 @@ 

Returns

def get_timestamp(self, obj)
-

get a utc timestamp from datetime/date/time objects

+

get a utc timestamp from datetime/date/time objects

Args

obj : datetime.datetime or datetime.date or datetime.time
@@ -1720,11 +1739,13 @@

Args

Returns

-
int or float: utc timestamp
-
 
-
+
int or float
+
utc timestamp
+
-Source code + +Expand source code +
def get_timestamp(self, obj):
     """
     get a utc timestamp from datetime/date/time objects
@@ -1743,7 +1764,7 @@ 

Returns

def to_raw(self, dt_obj)
-

get a utc timestamp from datetime object

+

get a utc timestamp from datetime object

Args

dt_obj : datetime.datetime or datetime.date or datetime.time
@@ -1751,11 +1772,13 @@

Args

Returns

-
int or float: utc timestamp
-
 
-
+
int or float
+
utc timestamp
+
-Source code + +Expand source code +
def to_raw(self, dt_obj):
     """
     get a utc timestamp from datetime object
@@ -1774,9 +1797,11 @@ 

Returns

def validate(self, value)
-
+
-Source code + +Expand source code +
def validate(self, value):
     if isinstance(self.from_raw(value), str):
         # cannot convert from string, still an invalid format
@@ -1792,7 +1817,7 @@ 

Returns

(default='', **kwargs)
-

Email field, will validate the value of emails

+

Email field, will validate the value of emails

will hold string email values.

Args

@@ -1800,9 +1825,11 @@

Args

default value. Defaults to ""
kwargs
other keyword arguments supported by string
-
+
-Source code + +Expand source code +
class Email(String):
     def __init__(self, default="", **kwargs):
         """
@@ -1843,16 +1870,18 @@ 

Methods

def validate(self, value)
-

check whether provided value is a valid email representation

+

check whether provided value is a valid email representation

Args

value (str)

Raises

-
ValidationError
+
ValidationError
in case the value is not a telephone
-
+
-Source code + +Expand source code +
def validate(self, value):
     """
     check whether provided value is a valid email representation
@@ -1885,9 +1914,9 @@ 

Inherited members

(enum_type, **kwargs)
-

Enum field, to be used with enum.Enum.

+

Enum field, to be used with enum.Enum.

Example:

-
class UserType(Enum):
+
class UserType(Enum):
     USER = "user"
     ADMIN = "admin"
 
@@ -1900,10 +1929,12 @@ 

Args

enum_type : type
enum type (class)
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Enum(Typed):
     def __init__(self, enum_type, **kwargs):
         """
@@ -1971,7 +2002,7 @@ 

Methods

def from_raw(self, value)
-

get an enum object from value

+

get an enum object from value

Args

value : any
@@ -1979,11 +2010,13 @@

Args

Returns

-
enum
+
enum
enum object of enum type of the field
-
+
-Source code + +Expand source code +
def from_raw(self, value):
     """
     get an enum object from value
@@ -2005,7 +2038,7 @@ 

Returns

def to_raw(self, enum_obj)
-

get enum value

+

get enum value

Args

enum_obj : enum.Enum
@@ -2013,11 +2046,13 @@

Args

Returns

-
any
+
any
enum value
-
+
-Source code + +Expand source code +
def to_raw(self, enum_obj):
     """
     get enum value
@@ -2048,16 +2083,16 @@ 

Inherited members

(type_, factory_type=None, **kwargs)
-

A factory field for any Base type, also, you can specify your factory type/class

+

A factory field for any Base type, also, you can specify your factory type/class

Example:

-
class User(Base):
+
class User(Base):
     name = fields.String()
 
 class Server(Base):
     users = fields.Factory(User)
 

Another example with a custom factory class:

-
class User(Base):
+
class User(Base):
     name = fields.String()
 
 class UserFactory(StoredFactory):
@@ -2074,9 +2109,11 @@ 

Args

type_ : Base
any base type to be used by the factory
-

factory_type (BaseFactory, optional): factory class/type. Defaults to None.

+

factory_type (BaseFactory, optional): factory class/type. Defaults to None.

-Source code + +Expand source code +
class Factory(Field):
     def __init__(self, type_, factory_type=None, **kwargs):
         """
@@ -2159,7 +2196,7 @@ 

Inherited members

(default=None, required=False, indexed=False, readonly=False, validators=None, stored=True, on_update=None, compute=None, **kwargs)
-

Base field for all field types, have some common options that can be used any other field type too.

+

Base field for all field types, have some common options that can be used any other field type too.

Args

default : any, optional
@@ -2178,9 +2215,11 @@

Args

a callable that takes the instance and new value on field updates. Defaults to None.
compute : callable, optional
a callable that takes the instance and returns a computed value for this field. on_update won't be called in this case. Defaults to None.
-
+
-Source code + +Expand source code +
class Field:
     def __init__(
         self,
@@ -2297,17 +2336,19 @@ 

Args

Subclasses

Instance variables

var computed
-
+
-Source code + +Expand source code +
@property
 def computed(self):
     return callable(self.compute)
@@ -2315,9 +2356,11 @@

Instance variables

var trigger_updates
-
+
-Source code + +Expand source code +
@property
 def trigger_updates(self):
     return callable(self.on_update)
@@ -2330,7 +2373,7 @@

Methods

def from_raw(self, value)
-

get the value of this field from primitive raw types

+

get the value of this field from primitive raw types

Args

value : any
@@ -2338,11 +2381,13 @@

Args

Returns

-
any
+
any
raw value
-
+
-Source code + +Expand source code +
def from_raw(self, value):
     """
     get the value of this field from primitive raw types
@@ -2360,9 +2405,11 @@ 

Returns

def preprocess(self, value)
-
+
-Source code + +Expand source code +
def preprocess(self, value):
     # TODO: make from/to raw methods only for serialization
     # and let preprocess/validate do the cleanup/checking step
@@ -2375,7 +2422,7 @@ 

Returns

def to_raw(self, value)
-

get the raw value of this field

+

get the raw value of this field

Args

value : any
@@ -2383,11 +2430,13 @@

Args

Returns

-
any
+
any
a primitive raw value
-
+
-Source code + +Expand source code +
def to_raw(self, value):
     """
     get the raw value of this field
@@ -2405,7 +2454,7 @@ 

Returns

def validate(self, value)
-

validate value if required and call custom self.validators if any

+

validate value if required and call custom self.validators if any

Args

value : any
@@ -2413,11 +2462,13 @@

Args

Raises

-
ValidationError
+
ValidationError
[description]
-
+
-Source code + +Expand source code +
def validate(self, value):
     """
     validate value if required and call custom self.validators if any
@@ -2440,7 +2491,7 @@ 

Raises

def validate_with_name(self, value, name)
-

validates the value using validate method and prepends the field name +

validates the value using validate method and prepends the field name in case an exception was thrown

Args

@@ -2451,11 +2502,13 @@

Args

(str): the field name

Raises

-
ValidationError
+
ValidationError
The original validation error with the field name prepended to the message
-
+
-Source code + +Expand source code +
def validate_with_name(self, value, name):
     """
     validates the value using validate method and prepends the field name
@@ -2482,7 +2535,7 @@ 

Raises

(default=0.0, **kwargs)
-

Same as Integer field, but with a type of float.

+

Same as Integer field, but with a type of float.

values can be set using strings like (will be converted to float):

  • "12.3", " 1212.23 @@ -2493,10 +2546,12 @@

    Args

    default : float, optional
    default value. Defaults to 0.0.
    kwargs
    -
    any keyword arguments supported by Field
    -
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Float(Typed):
     def __init__(self, default=0.0, **kwargs):
         """
@@ -2545,7 +2600,7 @@ 

Inherited members

(default=None, **kwargs)
-

UUID v4 field, will be auto-generated by default.

+

UUID v4 field, will be auto-generated by default.

Will hold the UUID as a string.

It accepts setting UUID value by many ways and converts them to strings:

    @@ -2559,10 +2614,12 @@

    Args

    default : str, optional
    default value, will be auto-generated if None. Defaults to None
    kwargs
    -
    other keyword arguments supported by String
    -
+
other keyword arguments supported by String
+
-Source code + +Expand source code +
class GUID(String):
     def __init__(self, default=None, **kwargs):
         """
@@ -2641,7 +2698,7 @@ 

Methods

def from_raw(self, value)
-

convert string, bytes, int to UUID object, then to a string

+

convert string, bytes, int to UUID object, then to a string

Args

value : int or str or bytes or uuid.UUID
@@ -2649,11 +2706,13 @@

Args

Returns

-
str
+
str
UUID string
-
+
-Source code + +Expand source code +
def from_raw(self, value):
     """
     convert string, bytes, int to UUID object, then to a string
@@ -2705,17 +2764,19 @@ 

Inherited members

(default='0.0.0.0', **kwargs)
-

IP address field, will validate the value of ip address (v4 and v6)

+

IP address field, will validate the value of ip address (v4 and v6)

Will hold string ip addresses.

Args

default : str, optional
default value. Defaults to ""
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class IPAddress(IPMixin, String):
     def __init__(self, default="0.0.0.0", **kwargs):
         """
@@ -2763,17 +2824,19 @@ 

Methods

def validate(self, value)
-

check whether provided value is a valid IPaddress representation +

check whether provided value is a valid IPaddress representation including IPv4,IPv6 and network

Args

value (str)

Raises

-
ValidationError
+
ValidationError
in case the value is not an IPAddress
-
+
-Source code + +Expand source code +
def validate(self, value):
     """
     check whether provided value is a valid IPaddress representation
@@ -2817,12 +2880,13 @@ 

Inherited members

class IPMixin -(*args, **kwargs)
-
+
-Source code + +Expand source code +
class IPMixin:
     def is_a(self, value, *types):
         """
@@ -2904,16 +2968,18 @@ 

Methods

def is_a(self, value, *types)
-

try creating any type of types from the given value

+

try creating any type of types from the given value

Args

value (str)

Returns

-
bool
+
bool
True if any matched, False otherwise
-
+
-Source code + +Expand source code +
def is_a(self, value, *types):
     """
     try creating any type of `types` from the given value
@@ -2940,7 +3006,7 @@ 

Returns

def is_iface(self, value)
-

check if a given value is an IP interface

+

check if a given value is an IP interface

Args

value : str
@@ -2948,11 +3014,13 @@

Args

Returns

-
bool
+
bool
True if it's a valid v4/v6 IP interface
-
+
-Source code + +Expand source code +
def is_iface(self, value):
     """
     check if a given value is an IP interface
@@ -2970,7 +3038,7 @@ 

Returns

def is_ip(self, value)
-

check if a given value is a v4/v6 IP address

+

check if a given value is a v4/v6 IP address

Args

value : str
@@ -2978,12 +3046,14 @@

Args

Returns

-
bool
+
bool
True if it's a valid v4/v6 IP address
-
+
-Source code + +Expand source code +
def is_ip(self, value):
     """
     check if a given value is a v4/v6 IP address
@@ -3001,7 +3071,7 @@ 

Returns

def is_ipv4(self, value)
-

check if a given value is a v4 IP address

+

check if a given value is a v4 IP address

Args

value : str
@@ -3009,11 +3079,13 @@

Args

Returns

-
bool
+
bool
True if it's a valid v4 IP address
-
+
-Source code + +Expand source code +
def is_ipv4(self, value):
     """
     check if a given value is a v4 IP address
@@ -3031,7 +3103,7 @@ 

Returns

def is_ipv6(self, value)
-

check if a given value is a v6 IP address

+

check if a given value is a v6 IP address

Args

value : str
@@ -3039,11 +3111,13 @@

Args

Returns

-
bool
+
bool
True if it's a valid v6 IP address
-
+
-Source code + +Expand source code +
def is_ipv6(self, value):
     """
     check if a given value is a v6 IP address
@@ -3064,17 +3138,19 @@ 

Returns

(default='::/128', **kwargs)
-

ip range field, will validate the value of ip ranges (v4 and v6)

+

ip range field, will validate the value of ip ranges (v4 and v6)

will be stored as a string.

Args

default : str, optional
default value. Defaults to ""
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class IPRange(IPMixin, String):
     def __init__(self, default="::/128", **kwargs):
         """
@@ -3117,17 +3193,19 @@ 

Methods

def validate(self, value)
-

check whether provided value is a valid IPaddress representation +

check whether provided value is a valid IPaddress representation including IPv4,IPv6 and network

Args

value (str)

Raises

-
ValidationError
+
ValidationError
in case the value is not an IPAddress
-
+
-Source code + +Expand source code +
def validate(self, value):
     """
     check whether provided value is a valid IPaddress representation
@@ -3171,7 +3249,7 @@ 

Inherited members

(default=0, min=None, max=None, **kwargs)
-

Intger field, the same as Typed, but with a type of int

+

Intger field, the same as Typed, but with a type of int

It can have a minimum value, if min is not set, it will ignore it.

values can be set using strings like (will be converted to int):

    @@ -3187,10 +3265,12 @@

    Args

    max : int, optional
    maximum value (inclusive). Defaults to None.
    kwargs
    -
    any keyword arguments supported by Field
    -
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Integer(Typed):
     def __init__(self, default=0, min=None, max=None, **kwargs):
         """
@@ -3256,17 +3336,19 @@ 

Inherited members

(default='{}', **kwargs)
-

Json field, will check if the value is a valid json string.

+

Json field, will check if the value is a valid json string.

It will hold json strings, if the value is not string, it will be converted.

Args

default : str, optional
default value. Defaults to "{}"
kwargs
-
other keyword arguments supported by String
-
+
other keyword arguments supported by String
+
-Source code + +Expand source code +
class Json(String):
     def __init__(self, default="{}", **kwargs):
         """
@@ -3330,7 +3412,7 @@ 

Methods

def from_raw(self, value)
-

convert non-string values to json string

+

convert non-string values to json string

Args

value : any
@@ -3338,11 +3420,13 @@

Args

Returns

-
str
+
str
a json string
-
+
-Source code + +Expand source code +
def from_raw(self, value):
     """
     convert non-string values to json string
@@ -3365,16 +3449,18 @@ 

Returns

def validate(self, value)
-

check whether provided value is a valid json

+

check whether provided value is a valid json

Args

value (str)

Raises

-
ValidationError
+
ValidationError
in case the value isn't a valid json
-
+
-Source code + +Expand source code +
def validate(self, value):
     """
     check whether provided value is a valid json
@@ -3413,16 +3499,18 @@ 

Inherited members

(field, **kwargs)
-

A list field for any field types.

+

A list field for any field types.

Args

-
field : Field
+
field : Field
a field instance of any fields, e.g. fields.String(maxlen=14).
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class List(Field):
     def __init__(self, field, **kwargs):
         """
@@ -3488,7 +3576,7 @@ 

Methods

def from_raw(self, values)
-

get a list of field type from raw values

+

get a list of field type from raw values

Args

values : list
@@ -3496,11 +3584,13 @@

Args

Returns

-
list
+
list
list of objects of field type
-
+
-Source code + +Expand source code +
def from_raw(self, values):
     """
     get a list of field type from raw values
@@ -3521,7 +3611,7 @@ 

Returns

def to_raw(self, values)
-

get a list of values as raw

+

get a list of values as raw

Args

values : list
@@ -3529,11 +3619,13 @@

Args

Returns

-
list
+
list
list of raw values
-
+
-Source code + +Expand source code +
def to_raw(self, values):
     """
     get a list of values as raw
@@ -3554,10 +3646,12 @@ 

Returns

def validate(self, value)
-

validate the value of every item in the list -Will just call the field.validate of the given field

+

validate the value of every item in the list +Will just call the field.validate of the given field

-Source code + +Expand source code +
def validate(self, value):
     """
     validate the value of every item in the list
@@ -3587,7 +3681,7 @@ 

Inherited members

(type_, type_kwargs=None, **kwargs)
-

An embedded Base object field of any type.

+

An embedded Base object field of any type.

Args

type_ : type
@@ -3595,10 +3689,12 @@

Args

type_kwargs : dict, optional
kwargs as a dict to be passed to Base instance when created. Defaults to None.
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Object(Typed):
     def __init__(self, type_, type_kwargs=None, **kwargs):
         """
@@ -3669,7 +3765,7 @@ 

Methods

def from_raw(self, data)
-

get an object from dict

+

get an object from dict

Args

data : dict
@@ -3677,11 +3773,13 @@

Args

Returns

-
Base
+
Base
base object
-
+
-Source code + +Expand source code +
def from_raw(self, data):
     """
     get an object from dict
@@ -3701,7 +3799,7 @@ 

Returns

def to_raw(self, obj)
-

get raw value of an object as dict

+

get raw value of an object as dict

Args

obj : Base
@@ -3709,11 +3807,13 @@

Args

Returns

-
dict
+
dict
raw data
-
+
-Source code + +Expand source code +
def to_raw(self, obj):
     """
     get raw value of an object as dict
@@ -3732,14 +3832,16 @@ 

Returns

def validate(self, value)
-

validate Base objects

+

validate Base objects

Args

value : Base
object
-
+
-Source code + +Expand source code +
def validate(self, value):
     """
     validate Base objects
@@ -3767,17 +3869,19 @@ 

Inherited members

(default='', **kwargs)
-

Path field, will validate the value of file system paths

+

Path field, will validate the value of file system paths

Will hold string path values.

Args

default : str, optional
default value. Defaults to ""
kwargs
-
other keyword arguments supported by Field
-
+
other keyword arguments supported by Field
+
-Source code + +Expand source code +
class Path(String):
     # TODO: Validate that it is working on windows
     def __init__(self, default="", **kwargs):
@@ -3819,16 +3923,18 @@ 

Methods

def validate(self, value)
-

check whether provided value is a valid path representation

+

check whether provided value is a valid path representation

Args

value (str)

Raises

-
ValidationError
+
ValidationError
in case the value is not a telephone
-
+
-Source code + +Expand source code +
def validate(self, value):
     """
     check whether provided value is a valid path representation
@@ -3861,15 +3967,17 @@ 

Inherited members

(**kwargs)
-

Port field, will check if the given port is within the range of 0-65535.

+

Port field, will check if the given port is within the range of 0-65535.

Will hold integers, but also accepts string values like "8080".

Args

kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Port(Integer):
     def __init__(self, **kwargs):
         """
@@ -3911,14 +4019,14 @@ 

Inherited members

(maxlen=None, allow_empty=True, **kwargs)
-

Same as String, but encrypted by default.

+

Same as String, but encrypted by default.

Should be used with sensitive data.

Args

kwargs
-
any keyword arguments supported by String
+
any keyword arguments supported by String
-

Same as Typed, but with a type of str.

+

Same as Typed, but with a type of str.

If maxlen is set, it will validate the length of the string.

Args

@@ -3927,10 +4035,12 @@

Args

allow_empty : bool
if empty string values are allowed or not. Defaults to True
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class Secret(String):
     """
     Same as `String`, but encrypted by default.
@@ -3964,7 +4074,7 @@ 

Inherited members

(maxlen=None, allow_empty=True, **kwargs)
-

Same as Typed, but with a type of str.

+

Same as Typed, but with a type of str.

If maxlen is set, it will validate the length of the string.

Args

@@ -3973,10 +4083,12 @@

Args

allow_empty : bool
if empty string values are allowed or not. Defaults to True
kwargs
-
any keyword arguments supported by Field
-
+
any keyword arguments supported by Field
+
-Source code + +Expand source code +
class String(Typed):
     def __init__(self, maxlen=None, allow_empty=True, **kwargs):
         """
@@ -4010,15 +4122,15 @@ 

Ancestors

Subclasses

Inherited members

    @@ -4037,7 +4149,7 @@

    Inherited members

    (default='', **kwargs)
    -

    Telephone field, will validate the value of telephone numbers

    +

    Telephone field, will validate the value of telephone numbers

    Will hold telephone values as strings.

    It will strip any additional characters that are not numbers.

    Args

    @@ -4045,10 +4157,12 @@

    Args

    default : str, optional
    default value. Defaults to ""
    kwargs
    -
    other keyword arguments supported by String
    -
    +
    other keyword arguments supported by String
    +
    -Source code + +Expand source code +
    class Tel(String):
         def __init__(self, default="", **kwargs):
             """
    @@ -4102,9 +4216,11 @@ 

    Methods

    def from_raw(self, value)
    -

    clean the telephone value from unwanted signs like , - ( )

    +

    clean the telephone value from unwanted signs like , - ( )

    -Source code + +Expand source code +
    def from_raw(self, value):
         """clean the telephone value from unwanted signs like , - ( )"""
         if value is not None:
    @@ -4121,16 +4237,18 @@ 

    Methods

    def validate(self, value)
    -

    check whether provided value is a valid telephone number representation

    +

    check whether provided value is a valid telephone number representation

    Args

    value (str)

    Raises

    -
    ValidationError
    +
    ValidationError
    in case the value is not a telephone
    -
    +
    -Source code + +Expand source code +
    def validate(self, value):
         """
         check whether provided value is a valid telephone number representation
    @@ -4162,7 +4280,7 @@ 

    Inherited members

    (default=None, format_=None, **kwargs)
    -

    time field, will hold utc datetime.time objects

    +

    time field, will hold utc datetime.time objects

    values can be set using strings in the given format_ too like "12:13" or a utc timestamp, they will converted to objects.

    Args

    @@ -4172,10 +4290,12 @@

    Args

    format_ : str, optional
    time format. Defaults to "%H:%M" if None.
    kwargs
    -
    any keyword arguments supported by Field
    -
    +
    any keyword arguments supported by Field
    +
    -Source code + +Expand source code +
    class Time(DateTimeMixin, Typed):
         def __init__(self, default=None, format_=None, **kwargs):
             """
    @@ -4223,16 +4343,18 @@ 

    Inherited members

    (type_, **kwargs)
    -

    Base field for any type, it will hold values of type_

    +

    Base field for any type, it will hold values of type_

    Args

    type_ : type
    any type (class)
    kwargs
    -
    any keyword arguments supported by Field
    -
    +
    any keyword arguments supported by Field
    +
    -Source code + +Expand source code +
    class Typed(Field):
         def __init__(self, type_, **kwargs):
             """
    @@ -4258,15 +4380,15 @@ 

    Ancestors

    Subclasses

    Inherited members

      @@ -4285,17 +4407,19 @@

      Inherited members

      (default='', **kwargs)
      -

      URL field, will validate the value of urls

      +

      URL field, will validate the value of urls

      Will hold string URLs.

      Args

      default : str, optional
      default value. Defaults to ""
      kwargs
      -
      other keyword arguments supported by Field
      -
      +
      other keyword arguments supported by Field
      +
      -Source code + +Expand source code +
      class URL(String):
           def __init__(self, default="", **kwargs):
               """
      @@ -4338,16 +4462,18 @@ 

      Methods

      def validate(self, value)
      -

      check whether provided value is a valid URL representation

      +

      check whether provided value is a valid URL representation

      Args

      value (str)

      Raises

      -
      ValidationError
      +
      ValidationError
      in case the value is not a telephone
      -
      +
      -Source code + +Expand source code +
      def validate(self, value):
           """
           check whether provided value is a valid URL representation
      @@ -4382,9 +4508,11 @@ 

      Inherited members

      (*args, **kwargs)
      -

      base type for any validation error

      +

      base type for any validation error

      -Source code + +Expand source code +
      class ValidationError(Exception):
           """
           base type for any validation error
      @@ -4565,9 +4693,7 @@ 

      -

      Generated by pdoc 0.6.4.

      +

      Generated by pdoc 0.10.0.

      - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/index.html b/docs/api/jumpscale/core/base/index.html index 3ab34716d..24d3cdbf8 100644 --- a/docs/api/jumpscale/core/base/index.html +++ b/docs/api/jumpscale/core/base/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.base API documentation - - - - - + + + + + + +
      @@ -21,7 +23,9 @@

      Module jumpscale.core.base

      -Source code + +Expand source code +
      from .meta import Base
       from .fields import ValidationError
       from .factory import Factory, StoredFactory, DuplicateError
      @@ -32,23 +36,23 @@

      Sub-modules

      jumpscale.core.base.events
      -

      Base and factory related events.

      +

      Base and factory related events.

      jumpscale.core.base.factory
      -

      Hierarchal configurations and factories for any Base type/class …

      +

      Hierarchal configurations and factories for any Base type/class …

      jumpscale.core.base.fields
      -

      jumpscale.core.base.fields have all possible fields where they can be used as a class variables for any Base classselfself …

      +

      jumpscale.core.base.fields have all possible fields where they can be used as a class variables for any Base classselfself …

      jumpscale.core.base.meta
      -

      Meta and Base classes for any class with fields …

      +

      Meta and Base classes for any class with fields …

      jumpscale.core.base.store
      -

      Store defines the interface for the backend storage, let it be filesystem or redis …

      +

      Store defines the interface for the backend storage, let it be filesystem or redis …

      @@ -83,9 +87,7 @@

      Index

      - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/meta.html b/docs/api/jumpscale/core/base/meta.html index bf22491d7..ea55a1f55 100644 --- a/docs/api/jumpscale/core/base/meta.html +++ b/docs/api/jumpscale/core/base/meta.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.meta API documentation - - - - - + + + + + + +
      @@ -23,23 +25,23 @@

      Module jumpscale.core.base.meta

      Meta and Base classes for any class with fields.

      Contains mainly:

        -
      • BaseMeta: A meta class to get a new class with field property descriptors ready
      • -
      • Base: The base class which can be used to get/set current field values
      • +
      • BaseMeta: A meta class to get a new class with field property descriptors ready
      • +
      • Base: The base class which can be used to get/set current field values

      To explain what it does, we will illustrate the following examples:

      If we have a class called Person, with the following definition:

      -
      class Person:
      +
      class Person:
           name = fields.String(default="ahmed")
       

      Accessing name from class or instance level will yield the same value, an instance of String field:

      -
      Person.name  #=> <jumpscale.core.base.fields.String object at 0x7efd89980c18>
      +
      Person.name  #=> <jumpscale.core.base.fields.String object at 0x7efd89980c18>
       
       p = Person()
       p.name  #=> <jumpscale.core.base.fields.String object at 0x7efd89980c18>
       

      The solution to this problem is using data descriptors (see https://docs.python.org/3/howto/descriptor.html)

      In meta and base implementations, we use property data descriptors, so the following class:

      -
      class Person(Base):
      +
      class Person(Base):
           name = fields.String(default="ahmed")
       

      Should have different behavior when accessing name from a class or an objects, so, it will be converted by meta class to a class like:

      @@ -58,15 +60,17 @@

      Module jumpscale.core.base.meta

      name = property(get_name, set_name)

      And accessing name from class and object levels will yield:

      -
      Person.name  #=> <property object at 0x7efd89a259f8>
      +
      Person.name  #=> <property object at 0x7efd89a259f8>
       
       
       p = Person()
       p.name  #=> "ahmed"
       
      -

      Parent relationship is supported too, every instance can have a parent object (which must be a Base type too)

      +

      Parent relationship is supported too, every instance can have a parent object (which must be a Base type too)

      -Source code + +Expand source code +
      """
       Meta and Base classes for any class with fields.
       
      @@ -592,16 +596,16 @@ 

      Module jumpscale.core.base.meta

      Functions

      -def get_field_property(name, field) +def get_field_property(name: str, field: Field) ‑> property
      -

      get a new property descriptor object for a field, +

      get a new property descriptor object for a field, this property will be used to enable getting/setting the actual value

      as the field only describes the type and other validation/conversion options, but do not hold the value itself, the vale will be held in the base instance

      the getter and setter will be called when an object is already created, and any field is accessed:

      -
      car = Car()
      +
      car = Car()
       print(car.color)  #=> getter will be called
       car.color = "red"  #=> setter will be called
       
      @@ -614,11 +618,13 @@

      Args

      Returns

      -
      property
      +
      property
      property descriptor (object)
      -
      +
      -Source code + +Expand source code +
      def get_field_property(name: str, field: fields.Field) -> property:
           """
           get a new property descriptor object for a field,
      @@ -696,11 +702,11 @@ 

      Classes

      (parent_=None, instance_name_=None, **values)
      -

      A simple attribute-based namespace.

      +

      A simple attribute-based namespace.

      SimpleNamespace(**kwargs)

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      any instance can have an optional name and a parent.

      -
      class Person(Base):
      +
      class Person(Base):
           name = fields.String()
           age = fields.Float()
       
      @@ -709,15 +715,17 @@ 

      Classes

      Args

      -
      parent_ : Base, optional
      +
      parent_ : Base, optional
      parent instance. Defaults to None.
      instance_name_ : str, optional
      instance name. Defaults to None.
      **values
      any given field values to initiate the instance with
      -
      +
      -Source code + +Expand source code +
      class Base(SimpleNamespace, metaclass=BaseMeta):
           def __init__(self, parent_=None, instance_name_=None, **values):
               """
      @@ -1043,8 +1051,8 @@ 

      Subclasses

      @@ -1054,8 +1062,8 @@

      Static methods

      def from_dict(data)
      -

      get an instance from a dict

      -
      class Person(Base):
      +

      get an instance from a dict

      +
      class Person(Base):
           name = fields.String()
           age = fields.Float()
       
      @@ -1069,11 +1077,13 @@ 

      Args

      Returns

      -
      Base
      -
      an instance from current Base type
      -
      +
      Base
      +
      an instance from current Base type
      +
      -Source code + +Expand source code +
      @classmethod
       def from_dict(cls, data):
           """
      @@ -1102,9 +1112,11 @@ 

      Instance variables

      var instance_name
      -
      +
      -Source code + +Expand source code +
      @property
       def instance_name(self):
           return self.__instance_name
      @@ -1112,9 +1124,11 @@

      Instance variables

      var parent
      -
      +
      -Source code + +Expand source code +
      @property
       def parent(self):
           return self.__parent
      @@ -1127,8 +1141,8 @@

      Methods

      def to_dict(self)
      -

      get a serializable dict from all values of all fields (except factories)

      -
      class Person(Base):
      +

      get a serializable dict from all values of all fields (except factories)

      +
      class Person(Base):
           name = fields.String()
           age = fields.Float()
       
      @@ -1138,11 +1152,13 @@ 

      Methods

      Returns

      -
      dict
      +
      dict
      data as dict with {name: value}
      -
      +
      -Source code + +Expand source code +
      def _get_data(self):
           """
           get a serializable dict from all values of all fields (except factories)
      @@ -1184,9 +1200,11 @@ 

      Returns

      def validate(self)
      -

      validate all fields of current instance

      +

      validate all fields of current instance

      -Source code + +Expand source code +
      def validate(self):
           """
           validate all fields of current instance
      @@ -1202,13 +1220,15 @@ 

      Returns

      (*args, **kwargs)
      -

      this class is used to get a new class with all field attributes replaced by property data descriptors.

      +

      this class is used to get a new class with all field attributes replaced by property data descriptors.

      this should be used as a metaclass, example:

      -
      class ExampleWithFields(metaclass=BaseMeta):
      +
      class ExampleWithFields(metaclass=BaseMeta):
           name = fields.String()
      -
      +
      -Source code + +Expand source code +
      class BaseMeta(type):
           """
           this class is used to get a new class with all field attributes replaced by property data descriptors.
      @@ -1308,9 +1328,7 @@ 

      -

      Generated by pdoc 0.6.4.

      +

      Generated by pdoc 0.10.0.

      - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/store/filesystem.html b/docs/api/jumpscale/core/base/store/filesystem.html index 4094aca35..207b7c5df 100644 --- a/docs/api/jumpscale/core/base/store/filesystem.html +++ b/docs/api/jumpscale/core/base/store/filesystem.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.store.filesystem API documentation - - - - - + + + + + + +
      @@ -21,7 +23,9 @@

      Module jumpscale.core.base.store.filesystem

      -Source code + +Expand source code +
      import os
       
       from . import ConfigNotFound, EncryptedConfigStore
      @@ -152,18 +156,20 @@ 

      Classes

      (location)
      -

      Filesystem store is an EncryptedConfigStore

      +

      Filesystem store is an EncryptedConfigStore

      It saves the config relative to config_env.get_store_config("filesystem")

      To store every instance config in a different path, it uses the given Location.

      -

      create a new FileSystemStore that stores config at the given location under configured root.

      -

      The root directory can be configured, see jumpscale.core.config

      +

      create a new FileSystemStore that stores config at the given location under configured root.

      +

      The root directory can be configured, see jumpscale.core.config

      Args

      location : Location
      where config will be stored per instance
      -
      +
      -Source code + +Expand source code +
      class FileSystemStore(EncryptedConfigStore):
           """
           Filesystem store is an EncryptedConfigStore
      @@ -282,14 +288,16 @@ 

      Instance variables

      var config_root
      -

      get the root directory where all configurations are written

      +

      get the root directory where all configurations are written

      Returns

      -
      str
      +
      str
      path
      -
      +
      -Source code + +Expand source code +
      @property
       def config_root(self):
           """
      @@ -308,11 +316,13 @@ 

      Methods

      def delete(self, instance_name)
      -

      delete instance config directory

      +

      delete instance config directory

      Args

      -

      instance_name (str):

      +

      instance_name (str):

      -Source code + +Expand source code +
      def delete(self, instance_name):
           """
           delete instance config directory
      @@ -329,7 +339,7 @@ 

      Args

      def get_instance_root(self, instance_name)
      -

      get the directory where instance config is written

      +

      get the directory where instance config is written

      Args

      instance_name : str
      @@ -337,11 +347,13 @@

      Args

      Returns

      -
      str
      +
      str
      path
      -
      +
      -Source code + +Expand source code +
      def get_instance_root(self, instance_name):
           """
           get the directory where instance config is written
      @@ -359,7 +371,7 @@ 

      Returns

      def get_path(self, instance_name)
      -

      get the path to data file where instance config is written

      +

      get the path to data file where instance config is written

      Args

      instance_name : str
      @@ -367,11 +379,13 @@

      Args

      Returns

      -
      str
      +
      str
      path
      -
      +
      -Source code + +Expand source code +
      def get_path(self, instance_name):
           """
           get the path to data file where instance config is written
      @@ -389,14 +403,16 @@ 

      Returns

      def list_all(self)
      -

      list all instance names (directories under config root)

      +

      list all instance names (directories under config root)

      Returns

      -
      list
      +
      list
      instance/directory names
      -
      +
      -Source code + +Expand source code +
      def list_all(self):
           """
           list all instance names (directories under config root)
      @@ -413,7 +429,7 @@ 

      Returns

      def read(self, instance_name)
      -

      read config data from the data file

      +

      read config data from the data file

      Args

      instance_name : str
      @@ -421,11 +437,13 @@

      Args

      Returns

      -
      str
      +
      str
      data
      -
      +
      -Source code + +Expand source code +
      def read(self, instance_name):
           """
           read config data from the data file
      @@ -446,7 +464,7 @@ 

      Returns

      def write(self, instance_name, data)
      -

      write config data to data file

      +

      write config data to data file

      Args

      instance_name : str
      @@ -456,11 +474,13 @@

      Args

      Returns

      -
      bool
      +
      bool
      written or not
      -
      +
      -Source code + +Expand source code +
      def write(self, instance_name, data):
           """
           write config data to data file
      @@ -525,9 +545,7 @@ 

      -

      Generated by pdoc 0.6.4.

      +

      Generated by pdoc 0.10.0.

      - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/store/index.html b/docs/api/jumpscale/core/base/store/index.html index f60ac3351..4ac45e098 100644 --- a/docs/api/jumpscale/core/base/store/index.html +++ b/docs/api/jumpscale/core/base/store/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.store API documentation - - - - - + + + + + + +
      @@ -25,7 +27,9 @@

      Module jumpscale.core.base.store

      Every backend should be able to organize configuration for multiple instance given a location, also read/write the config data in raw (string) format.

      -Source code + +Expand source code +
      """
       Store defines the interface for the backend storage, let it be filesystem or redis.
       
      @@ -359,19 +363,19 @@ 

      Sub-modules

      jumpscale.core.base.store.filesystem
      -
      +
      jumpscale.core.base.store.redis
      -
      +
      jumpscale.core.base.store.serializers
      -
      +
      jumpscale.core.base.store.whooshfts
      -
      +

      @@ -387,9 +391,11 @@

      Classes

      (*args, **kwargs)
      -

      raised when a config is not found for an instance

      +

      raised when a config is not found for an instance

      -Source code + +Expand source code +
      class ConfigNotFound(Exception):
           """
           raised when a config is not found for an instance
      @@ -403,10 +409,9 @@ 

      Ancestors

      class ConfigStore -(*args, **kwargs)
      -

      the interface every config store should implement:

      +

      the interface every config store should implement:

      • read(instance_name): reads the data of this instance name
      • @@ -414,9 +419,11 @@

        Ancestors

      • list_all(instance_name): lists all instance names
      • delete(instance_name): delete instance data
      • find(self, cursor_=None, limit_=None, **query): optional search method with query as field mapping
      • -
      +

    -Source code + +Expand source code +
    class ConfigStore(ABC):
         """
         the interface every config store should implement:
    @@ -462,9 +469,11 @@ 

    Methods

    def delete(self, instance_name)
    -
    +
    -Source code + +Expand source code +
    @abstractmethod
     def delete(self, instance_name):
         pass
    @@ -474,9 +483,11 @@

    Methods

    def find(self, cursor_=None, limit_=None, **query)
    -
    +
    -Source code + +Expand source code +
    @abstractmethod
     def find(self, cursor_=None, limit_=None, **query):
         pass
    @@ -486,9 +497,11 @@

    Methods

    def list_all(self)
    -
    +
    -Source code + +Expand source code +
    @abstractmethod
     def list_all(self):
         pass
    @@ -498,9 +511,11 @@

    Methods

    def read(self, instance_name)
    -
    +
    -Source code + +Expand source code +
    @abstractmethod
     def read(self, instance_name):
         pass
    @@ -510,9 +525,11 @@

    Methods

    def write(self, instance_name, data)
    -
    +
    -Source code + +Expand source code +
    @abstractmethod
     def write(self, instance_name, data):
         pass
    @@ -525,18 +542,20 @@

    Methods

    (location, serializer)
    -

    the base class for any config store backend

    +

    the base class for any config store backend

    the base for encrypted config store

    Args

    location (Location) serializer (Serializer)

    Raises

    -
    InvalidPrivateKey
    +
    InvalidPrivateKey
    in case the private key is not configured
    -
    +
    -Source code + +Expand source code +
    class EncryptedConfigStore(ConfigStore, EncryptionMixin):
         """the base class for any config store backend"""
     
    @@ -710,7 +729,7 @@ 

    Methods

    def find(self, cursor_=None, limit_=None, **query)
    -

    a generic find, which do a linear search over all items

    +

    a generic find, which do a linear search over all items

    if you want a better way, use a store which provides search

    Args

    @@ -723,11 +742,13 @@

    Args

    Returns

    -
    tuple
    +
    tuple
    the new cursor, total result count and a generator for results
    -
    +
    -Source code + +Expand source code +
    def find(self, cursor_=None, limit_=None, **query):
         """
         a generic find, which do a linear search over all items
    @@ -791,7 +812,7 @@ 

    Returns

    def get(self, instance_name)
    -

    get instance config

    +

    get instance config

    Args

    instance_name : str
    @@ -799,11 +820,13 @@

    Args

    Returns

    -
    dict
    +
    dict
    instance config as dict
    -
    +
    -Source code + +Expand source code +
    def get(self, instance_name):
         """
         get instance config
    @@ -822,7 +845,7 @@ 

    Returns

    def save(self, instance_name, config)
    -

    save instance config

    +

    save instance config

    Args

    instance_name : str
    @@ -832,11 +855,13 @@

    Args

    Returns

    -
    bool
    +
    bool
    written or not
    -
    +
    -Source code + +Expand source code +
    def save(self, instance_name, config):
         """
         save instance config
    @@ -865,12 +890,13 @@ 

    Inherited members

    class EncryptionMixin -(*args, **kwargs)
    -

    A mixin that provides encrypt and decrypt methods, which can be used in any store

    +

    A mixin that provides encrypt and decrypt methods, which can be used in any store

    -Source code + +Expand source code +
    class EncryptionMixin:
         """
         A mixin that provides encrypt and decrypt methods, which can be used in any store
    @@ -910,7 +936,7 @@ 

    Methods

    def decrypt(self, data)
    -

    decrypt data

    +

    decrypt data

    Args

    data : bytes
    @@ -918,11 +944,13 @@

    Args

    Returns

    -
    str
    +
    str
    decrypted data
    -
    +
    -Source code + +Expand source code +
    def decrypt(self, data):
         """decrypt data
     
    @@ -939,7 +967,7 @@ 

    Returns

    def encrypt(self, data)
    -

    encrypt data

    +

    encrypt data

    Args

    data : str
    @@ -947,11 +975,13 @@

    Args

    Returns

    -
    bytes
    +
    bytes
    encrypted data as byte string
    -
    +
    -Source code + +Expand source code +
    def encrypt(self, data):
         """encrypt data
     
    @@ -970,12 +1000,14 @@ 

    Returns

    class EncryptionMode -(*args, **kwargs) +(value, names=None, *, module=None, qualname=None, type=None, start=1)
    -

    An enum to select encryption mode based on loading or storing the data

    +

    An enum to select encryption mode based on loading or storing the data

    -Source code + +Expand source code +
    class EncryptionMode(Enum):
         """
         An enum to select encryption mode based on loading or storing the data
    @@ -992,11 +1024,11 @@ 

    Class variables

    var Decrypt
    -
    +
    var Encrypt
    -
    +
    @@ -1005,9 +1037,11 @@

    Class variables

    (*args, **kwargs)
    -

    raised when the private key configured is invalid

    +

    raised when the private key configured is invalid

    -Source code + +Expand source code +
    class InvalidPrivateKey(Exception):
         """
         raised when the private key configured is invalid
    @@ -1024,11 +1058,13 @@ 

    Ancestors

    (*name_list, type_=None)
    -

    dot-separated auto-location for any type

    +

    dot-separated auto-location for any type

    for example, if we have a class in jumpscale/clients/redis/ -location name will be jumpscale.clients.redis.

    +location name will be jumpscale.clients.redis.

    -Source code + +Expand source code +
    class Location:
         """
         dot-separated auto-location for any type
    @@ -1093,7 +1129,7 @@ 

    Static methods

    def from_type(type_)
    -

    get a location from any type/class

    +

    get a location from any type/class

    Args

    type_ : type
    @@ -1101,11 +1137,13 @@

    Args

    Returns

    -
    Location
    +
    Location
    a location object
    -
    +
    -Source code + +Expand source code +
    @classmethod
     def from_type(cls, type_):
         """
    @@ -1125,14 +1163,16 @@ 

    Instance variables

    var name
    -

    get dot seprated string from current name list

    +

    get dot seprated string from current name list

    Returns

    -
    str
    +
    str
    dot separated string
    -
    +
    -Source code + +Expand source code +
    @property
     def name(self):
         """
    @@ -1146,14 +1186,16 @@ 

    Returns

    var path
    -

    get a filesystem path with from name, where dots are replaced by os.sep

    +

    get a filesystem path with from name, where dots are replaced by os.sep

    Returns

    -
    str
    +
    str
    path
    -
    +
    -Source code + +Expand source code +
    @property
     def path(self):
         """
    @@ -1172,9 +1214,11 @@ 

    Returns

    (*args, **kwargs)
    -

    raised by store backends

    +

    raised by store backends

    -Source code + +Expand source code +
    class StoreException(Exception):
         """
         raised by store backends
    @@ -1265,9 +1309,7 @@ 

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/store/redis.html b/docs/api/jumpscale/core/base/store/redis.html index c8132888d..55f58d0d5 100644 --- a/docs/api/jumpscale/core/base/store/redis.html +++ b/docs/api/jumpscale/core/base/store/redis.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.store.redis API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.core.base.store.redis

    -Source code + +Expand source code +
    import redis
     
     from . import ConfigNotFound, EncryptedConfigStore
    @@ -162,14 +166,16 @@ 

    Classes

    (location)
    -

    RedisStore store is an EncryptedConfigStore

    +

    RedisStore store is an EncryptedConfigStore

    It saves the data in redis and configuration for redis comes from config_env.get_store_config("redis")

    create a new redis store, the location given will be used to generate keys

    this keys will be combined to get/set instance config

    Args

    -

    location (Location)

    +

    location (Location)

    -Source code + +Expand source code +
    class RedisStore(EncryptedConfigStore):
         """
         RedisStore store is an EncryptedConfigStore
    @@ -302,19 +308,18 @@ 

    Methods

    def delete(self, instance_name)
    -

    delete given instance

    +

    delete given instance

    Args

    instance_name : str
    name

    Returns

    -
    -
    bool
    -
     
    -
    +

    bool

    -Source code + +Expand source code +
    def delete(self, instance_name):
         """
         delete given instance
    @@ -332,7 +337,7 @@ 

    Returns

    def get_key(self, instance_name)
    -

    get a key for an instance

    +

    get a key for an instance

    this will return a dot-separated key derived from current location

    Args

    @@ -341,11 +346,13 @@

    Args

    Returns

    -
    str
    +
    str
    key
    -
    +
    -Source code + +Expand source code +
    def get_key(self, instance_name):
         """
         get a key for an instance
    @@ -365,14 +372,16 @@ 

    Returns

    def get_location_keys(self)
    -

    get all keys under current location (scanned)

    +

    get all keys under current location (scanned)

    Returns

    -
    list
    +
    list
    a list of keys
    -
    +
    -Source code + +Expand source code +
    def get_location_keys(self):
         """
         get all keys under current location (scanned)
    @@ -387,10 +396,16 @@ 

    Returns

    def list_all(self)
    -

    get all names of instances (instance keys)

    -

    Returns

    +

    get all names of instances (instance keys)

    +

    Returns

    +
    +
    [type]
    +
    [description]
    +
    -Source code + +Expand source code +
    def list_all(self):
         """
         get all names of instances (instance keys)
    @@ -414,7 +429,7 @@ 

    Returns

    def read(self, instance_name)
    -

    read instance config from redis

    +

    read instance config from redis

    Args

    instance_name : name
    @@ -422,11 +437,13 @@

    Args

    Returns

    -
    str
    +
    str
    data
    -
    +
    -Source code + +Expand source code +
    def read(self, instance_name):
         """
         read instance config from redis
    @@ -447,7 +464,7 @@ 

    Returns

    def write(self, instance_name, data)
    -

    set data with the corresponding key for this instance

    +

    set data with the corresponding key for this instance

    Args

    instance_name : str
    @@ -457,11 +474,13 @@

    Args

    Returns

    -
    bool
    +
    bool
    written or not
    -
    +
    -Source code + +Expand source code +
    def write(self, instance_name, data):
         """
         set data with the corresponding key for this instance
    @@ -523,9 +542,7 @@ 

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/store/serializers.html b/docs/api/jumpscale/core/base/store/serializers.html index 8ec28c489..f4e126583 100644 --- a/docs/api/jumpscale/core/base/store/serializers.html +++ b/docs/api/jumpscale/core/base/store/serializers.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.store.serializers API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.core.base.store.serializers

    -Source code + +Expand source code +
    from jumpscale.data.serializers import json
     
     
    @@ -52,12 +56,13 @@ 

    Classes

    class JsonSerializer -(*args, **kwargs)
    -
    +
    -Source code + +Expand source code +
    class JsonSerializer(Serializer):
         def serialize(self, obj):
             return json.dumps(obj)
    @@ -75,9 +80,11 @@ 

    Methods

    def deserialize(self, data)
    -
    +
    -Source code + +Expand source code +
    def deserialize(self, data):
         return json.loads(data)
    @@ -86,9 +93,11 @@

    Methods

    def serialize(self, obj)
    -
    +
    -Source code + +Expand source code +
    def serialize(self, obj):
         return json.dumps(obj)
    @@ -97,12 +106,13 @@

    Methods

    class Serializer -(*args, **kwargs)
    -
    +
    -Source code + +Expand source code +
    class Serializer:
         def serialize(self, obj):
             return obj
    @@ -120,9 +130,11 @@ 

    Methods

    def deserialize(self, data)
    -
    +
    -Source code + +Expand source code +
    def deserialize(self, data):
         return data
    @@ -131,9 +143,11 @@

    Methods

    def serialize(self, obj)
    -
    +
    -Source code + +Expand source code +
    def serialize(self, obj):
         return obj
    @@ -176,9 +190,7 @@

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/base/store/whooshfts.html b/docs/api/jumpscale/core/base/store/whooshfts.html index a12c6c71b..fd6331b5b 100644 --- a/docs/api/jumpscale/core/base/store/whooshfts.html +++ b/docs/api/jumpscale/core/base/store/whooshfts.html @@ -3,15 +3,17 @@ - + jumpscale.core.base.store.whooshfts API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.core.base.store.whooshfts

    -Source code + +Expand source code +
    from whoosh import fields
     from whoosh.index import create_in, exists_in, open_dir
     from whoosh.qparser import FuzzyTermPlugin, GtLtPlugin, MultifieldParser, PhrasePlugin
    @@ -236,14 +240,16 @@ 

    Classes

    (location)
    -

    whoosh store is an EncryptedConfigStore

    +

    whoosh store is an EncryptedConfigStore

    It saves and indexes the data in a whoosh index

    create a new redis store, the location given will be used to generate keys

    this keys will be combined to get/set instance config

    Args

    -

    location (Location)

    +

    location (Location)

    -Source code + +Expand source code +
    class WhooshStore(EncryptedConfigStore):
         """
         whoosh store is an EncryptedConfigStore
    @@ -412,9 +418,11 @@ 

    Instance variables

    var index_path
    -
    +
    -Source code + +Expand source code +
    @property
     def index_path(self):
         path = join_paths(self.base_index_path, self.location.name)
    @@ -424,9 +432,11 @@ 

    Instance variables

    var type_fields
    -
    +
    -Source code + +Expand source code +
    @property
     def type_fields(self):
         return self.location.type._fields.items()
    @@ -439,9 +449,11 @@

    Methods

    def delete(self, instance_name)
    -
    +
    -Source code + +Expand source code +
    def delete(self, instance_name):
         writer = self.get_writer()
         writer.delete_by_term(KEY_FIELD_NAME, instance_name)
    @@ -452,9 +464,11 @@ 

    Methods

    def get_index(self, schema)
    -
    +
    -Source code + +Expand source code +
    def get_index(self, schema):
         if exists_in(self.index_path):
             return open_dir(self.index_path, schema=schema)
    @@ -465,9 +479,11 @@ 

    Methods

    def get_reader(self)
    -
    +
    -Source code + +Expand source code +
    def get_reader(self):
         return self.index.reader()
    @@ -476,9 +492,11 @@

    Methods

    def get_schema(self)
    -
    +
    -Source code + +Expand source code +
    def get_schema(self):
         schema_fields = {
             KEY_FIELD_NAME: fields.ID(unique=True, stored=True),
    @@ -509,9 +527,11 @@ 

    Methods

    def get_searcher(self, up_to_date=True)
    -
    +
    -Source code + +Expand source code +
    def get_searcher(self, up_to_date=True):
         searcher = self.index.searcher()
     
    @@ -525,9 +545,11 @@ 

    Methods

    def get_writer(self)
    -
    +
    -Source code + +Expand source code +
    def get_writer(self):
         return AsyncWriter(self.index)
    @@ -536,9 +558,11 @@

    Methods

    def list_all(self)
    -
    +
    -Source code + +Expand source code +
    def list_all(self):
         with self.get_reader() as reader:
             for _, doc in reader.iter_docs():
    @@ -549,9 +573,11 @@ 

    Methods

    def read(self, instance_name)
    -
    +
    -Source code + +Expand source code +
    def read(self, instance_name):
         with self.get_searcher() as searcher:
             kw = {KEY_FIELD_NAME: instance_name}
    @@ -580,9 +606,11 @@ 

    Methods

    def write(self, instance_name, data)
    -
    +
    -Source code + +Expand source code +
    def write(self, instance_name, data):
         data[KEY_FIELD_NAME] = instance_name
     
    @@ -652,9 +680,7 @@ 

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/config/config.html b/docs/api/jumpscale/core/config/config.html index 65c45d6df..b0f08b6de 100644 --- a/docs/api/jumpscale/core/config/config.html +++ b/docs/api/jumpscale/core/config/config.html @@ -3,15 +3,17 @@ - + jumpscale.core.config.config API documentation - - - - - + + + + + + +
    @@ -23,9 +25,9 @@

    Module jumpscale.core.config.config

    Config module is the single entry of configurations across the framework.

    It allows - resolving configurations paths for configuration directoryconfig_root, or configuration file path config_path -- rebuildling default configurations or retrieving them (using get_default_config()) -- Getting configurations using get_default_config() -- Updating configuration using update_config()

    +- rebuildling default configurations or retrieving them (using get_default_config()) +- Getting configurations using get_default_config() +- Updating configuration using update_config()

    JS-NG> j.core.config

    Getting where is the config.toml path

    @@ -65,7 +67,9 @@

    Get/Set

    'grey'

    -Source code + +Expand source code +
    """Config module is the single entry of configurations across the framework.
     
     It allows
    @@ -359,16 +363,14 @@ 

    Functions

    def get(key, default=None)
    -

    Retrives value from jumpscale config

    +

    Retrives value from jumpscale config

    Arguments

    -
    -
    key : str
    -
    the key you wish to retrieve
    -
    default : object
    -
    return value if key doesn't exist in configurations
    -
    +

    key (str): the key you wish to retrieve +default (object): return value if key doesn't exist in configurations

    -Source code + +Expand source code +
    def get(key, default=None):
         """ Retrives value from jumpscale config
     
    @@ -384,11 +386,13 @@ 

    Arguments

    def get_config()
    -

    Gets jumpscale configurations

    +

    Gets jumpscale configurations

    Returns

    -

    [dict] - toml loaded config of CONFIG_DIR/config.toml

    +

    [dict] - toml loaded config of CONFIG_DIR/config.toml

    -Source code + +Expand source code +
    def get_config():
         """Gets jumpscale configurations
     
    @@ -403,9 +407,11 @@ 

    Returns

    def get_current_version()
    -
    +
    -Source code + +Expand source code +
    def get_current_version():
         return __version__
    @@ -414,14 +420,16 @@

    Returns

    def get_default_config()
    -

    retrieves default configurations for plain jumpscale

    +

    retrieves default configurations for plain jumpscale

    Returns

    -
    dict
    +
    dict
    default configuration
    -
    +
    -Source code + +Expand source code +
    def get_default_config():
         """retrieves default configurations for plain jumpscale
     
    @@ -466,16 +474,14 @@ 

    Returns

    def set(key, val)
    -

    Sets value in jumpscale config

    +

    Sets value in jumpscale config

    Arguments

    -
    -
    key : str
    -
    the key you wish to update
    -
    val
    -
    value to update with
    -
    +

    key (str): the key you wish to update +val: value to update with

    -Source code + +Expand source code +
    def set(key, val):
         """ Sets value in jumpscale config
     
    @@ -492,21 +498,16 @@ 

    Arguments

    def set_default(key, val)
    -

    Sets key to value in jumpscale config and returns

    +

    Sets key to value in jumpscale config and returns

    Arguments

    -
    -
    key : str
    -
    the key you wish to update
    -
    val
    -
    value to update with and to return if key doesn't exist in configurations
    -
    +

    key (str): the key you wish to update +val: value to update with and to return if key doesn't exist in configurations

    Returns

    -
    -
    val : str
    -
    returned if key doesn't exist in configuration or the value of key in configurations
    -
    +

    val (str): returned if key doesn't exist in configuration or the value of key in configurations

    -Source code + +Expand source code +
    def set_default(key, val):
         """ Sets key to value in jumpscale config and returns
     
    @@ -529,11 +530,13 @@ 

    Returns

    def update_config(data)
    -

    Update jumpscale config with new data

    +

    Update jumpscale config with new data

    Arguments

    -

    data {dict} – dict to update the config with.

    +

    data {dict} – dict to update the config with.

    -Source code + +Expand source code +
    def update_config(data):
         """Update jumpscale config with new data
     
    @@ -551,12 +554,13 @@ 

    Classes

    class Environment -(*args, **kwargs)
    -
    +
    -Source code + +Expand source code +
    class Environment:
         def get_private_key_path(self):
             config = get_config()
    @@ -593,9 +597,11 @@ 

    Methods

    def get_logging_config(self)
    -
    +
    -Source code + +Expand source code +
    def get_logging_config(self):
         return get_config()["logging"]
    @@ -604,9 +610,11 @@

    Methods

    def get_private_key(self)
    -
    +
    -Source code + +Expand source code +
    def get_private_key(self):
         private_key_path = self.get_private_key_path()
         if not private_key_path:
    @@ -622,9 +630,11 @@ 

    Methods

    def get_private_key_path(self)
    -
    +
    -Source code + +Expand source code +
    def get_private_key_path(self):
         config = get_config()
         private_key_path = config["private_key_path"]
    @@ -635,9 +645,11 @@ 

    Methods

    def get_store_config(self, name)
    -
    +
    -Source code + +Expand source code +
    def get_store_config(self, name):
         config = get_config()
         stores = config["stores"]
    @@ -650,9 +662,11 @@ 

    Methods

    def get_threebot_data(self)
    -
    +
    -Source code + +Expand source code +
    def get_threebot_data(self):
         config = get_config()
         return config.get("threebot", {})
    @@ -710,9 +724,7 @@

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/config/index.html b/docs/api/jumpscale/core/config/index.html index 05aa78b4c..6337ebfd3 100644 --- a/docs/api/jumpscale/core/config/index.html +++ b/docs/api/jumpscale/core/config/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.config API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.core.config

    -Source code + +Expand source code +
    from .config import *
    @@ -30,7 +34,7 @@

    Sub-modules

    jumpscale.core.config.config
    -

    Config module is the single entry of configurations across the framework …

    +

    Config module is the single entry of configurations across the framework …

    @@ -61,9 +65,7 @@

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/db/index.html b/docs/api/jumpscale/core/db/index.html index f3bcb29fd..afc092899 100644 --- a/docs/api/jumpscale/core/db/index.html +++ b/docs/api/jumpscale/core/db/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.db API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.core.db

    -Source code + +Expand source code +
    def export_module_as():
     
         from jumpscale.loader import j
    @@ -51,9 +55,11 @@ 

    Functions

    def export_module_as()
    -
    +
    -Source code + +Expand source code +
    def export_module_as():
     
         from jumpscale.loader import j
    @@ -97,9 +103,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/dirs/dirs.html b/docs/api/jumpscale/core/dirs/dirs.html index 25f3c0b49..a910fda39 100644 --- a/docs/api/jumpscale/core/dirs/dirs.html +++ b/docs/api/jumpscale/core/dirs/dirs.html @@ -3,15 +3,17 @@ - + jumpscale.core.dirs.dirs API documentation - - - - - + + + + + + +
    @@ -21,76 +23,78 @@

    Module jumpscale.core.dirs.dirs

    This module defines main dirs in jumpscale to be used

    -
    JS-NG> j.core.dirs                                                                                                                                                        
    +
    JS-NG> j.core.dirs
     ExportedModule(__doc__=None, _exportedas=<class 'jumpscale.core.dirs.dirs.Dirs'>, _loaded=True, _m=<module 'jumpscale.core.dirs' from '/home/ahmed/wspace/js/js-ng/jumpscale/core/dirs/__init__.py'>)
     
    -JS-NG> j.core.dirs.BASEDIR                                                                                                                                                
    +JS-NG> j.core.dirs.BASEDIR
     '/home/ahmed/sandbox'
     
    -JS-NG> j.core.dirs.BINDIR                                                                                                                                                 
    +JS-NG> j.core.dirs.BINDIR
     '/home/ahmed/sandbox/bin'
     
    -JS-NG>                                                                                                                                                                    
    -JS-NG> j.core.dirs.CFGDIR                                                                                                                                                 
    +JS-NG>
    +JS-NG> j.core.dirs.CFGDIR
     '/home/ahmed/sandbox/cfg'
     
    -JS-NG>                                                                                                                                                                    
    -JS-NG> j.core.dirs.CODEDIR                                                                                                                                                
    +JS-NG>
    +JS-NG> j.core.dirs.CODEDIR
     '/home/ahmed/sandbox/code'
     
    -JS-NG>                                                                                                                                                                    
    -JS-NG> j.core.dirs.HOMEDIR                                                                                                                                                
    +JS-NG>
    +JS-NG> j.core.dirs.HOMEDIR
     '/home/ahmed'
     
    -JS-NG> j.core.dirs.LOGDIR                                                                                                                                                 
    +JS-NG> j.core.dirs.LOGDIR
     '/home/ahmed/sandbox/var/log'
     
    -JS-NG> j.core.dirs.TEMPLATEDIR                                                                                                                                            
    +JS-NG> j.core.dirs.TEMPLATEDIR
     '/home/ahmed/sandbox/var/templates'
     
    -JS-NG> j.core.dirs.TMPDIR                                                                                                                                                 
    +JS-NG> j.core.dirs.TMPDIR
     '/tmp/jumpscale'
     
    -JS-NG> j.core.dirs.VARDIR                                                                                                                                                 
    +JS-NG> j.core.dirs.VARDIR
     '/home/ahmed/sandbox/var'
     
    -Source code + +Expand source code +
    """
     This module defines main dirs in jumpscale to be used
     
     ```
    -JS-NG> j.core.dirs                                                                                                                                                        
    +JS-NG> j.core.dirs
     ExportedModule(__doc__=None, _exportedas=<class 'jumpscale.core.dirs.dirs.Dirs'>, _loaded=True, _m=<module 'jumpscale.core.dirs' from '/home/ahmed/wspace/js/js-ng/jumpscale/core/dirs/__init__.py'>)
     
    -JS-NG> j.core.dirs.BASEDIR                                                                                                                                                
    +JS-NG> j.core.dirs.BASEDIR
     '/home/ahmed/sandbox'
     
    -JS-NG> j.core.dirs.BINDIR                                                                                                                                                 
    +JS-NG> j.core.dirs.BINDIR
     '/home/ahmed/sandbox/bin'
     
    -JS-NG>                                                                                                                                                                    
    -JS-NG> j.core.dirs.CFGDIR                                                                                                                                                 
    +JS-NG>
    +JS-NG> j.core.dirs.CFGDIR
     '/home/ahmed/sandbox/cfg'
     
    -JS-NG>                                                                                                                                                                    
    -JS-NG> j.core.dirs.CODEDIR                                                                                                                                                
    +JS-NG>
    +JS-NG> j.core.dirs.CODEDIR
     '/home/ahmed/sandbox/code'
     
    -JS-NG>                                                                                                                                                                    
    -JS-NG> j.core.dirs.HOMEDIR                                                                                                                                                
    +JS-NG>
    +JS-NG> j.core.dirs.HOMEDIR
     '/home/ahmed'
     
    -JS-NG> j.core.dirs.LOGDIR                                                                                                                                                 
    +JS-NG> j.core.dirs.LOGDIR
     '/home/ahmed/sandbox/var/log'
     
    -JS-NG> j.core.dirs.TEMPLATEDIR                                                                                                                                            
    +JS-NG> j.core.dirs.TEMPLATEDIR
     '/home/ahmed/sandbox/var/templates'
     
    -JS-NG> j.core.dirs.TMPDIR                                                                                                                                                 
    +JS-NG> j.core.dirs.TMPDIR
     '/tmp/jumpscale'
     
    -JS-NG> j.core.dirs.VARDIR                                                                                                                                                 
    +JS-NG> j.core.dirs.VARDIR
     '/home/ahmed/sandbox/var'
     ```
     
    @@ -125,12 +129,13 @@ 

    Classes

    class Dirs -(*args, **kwargs)
    -
    +
    -Source code + +Expand source code +
    class Dirs:
     
         HOMEDIR = os.path.expanduser("~")  # TODO: check homedir defined in sal.fs
    @@ -148,43 +153,43 @@ 

    Class variables

    var BASEDIR
    -
    +
    var BINDIR
    -
    +
    var CFGDIR
    -
    +
    var CODEDIR
    -
    +
    var HOMEDIR
    -
    +
    var JSCFGDIR
    -
    +
    var LOGDIR
    -
    +
    var TEMPLATEDIR
    -
    +
    var TMPDIR
    -
    +
    var VARDIR
    -
    +
    @@ -225,9 +230,7 @@

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/dirs/index.html b/docs/api/jumpscale/core/dirs/index.html index ff8422da3..6c3b4be90 100644 --- a/docs/api/jumpscale/core/dirs/index.html +++ b/docs/api/jumpscale/core/dirs/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.dirs API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.core.dirs

    -Source code + +Expand source code +
    def export_module_as():
         from .dirs import Dirs
     
    @@ -33,7 +37,7 @@ 

    Sub-modules

    jumpscale.core.dirs.dirs
    -

    This module defines main dirs in jumpscale to be used …

    +

    This module defines main dirs in jumpscale to be used …

    @@ -46,9 +50,11 @@

    Functions

    def export_module_as()
    -
    +
    -Source code + +Expand source code +
    def export_module_as():
         from .dirs import Dirs
     
    @@ -85,9 +91,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/events/index.html b/docs/api/jumpscale/core/events/index.html index 9fef5f900..7c0b0de7e 100644 --- a/docs/api/jumpscale/core/events/index.html +++ b/docs/api/jumpscale/core/events/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.events API documentation - - - - - + + + + + + +
    @@ -24,7 +26,7 @@

    Module jumpscale.core.events

    The modules is based on https://github.com/xmonader/events.

    Events can be of any type (class), there are two ways for listening to an event:

    Using decorators:

    -
    from jumpscale.core.base import events
    +
    from jumpscale.core.base import events
     
     class ImportantEvent:
         def __init__(self):
    @@ -35,8 +37,8 @@ 

    Module jumpscale.core.events

    def handle_event(ev): print(ev.name)
    -

    Or using it in your classes, just inherit from events.Handler and implement handle method.

    -
    class Impl(events.Handler):
    +

    Or using it in your classes, just inherit from events.Handler and implement handle() method.

    +
    class Impl(events.Handler):
         def __init__(self):
             events.add_listeners(self, ImprotantEvent)
     
    @@ -51,7 +53,9 @@ 

    Module jumpscale.core.events

    This can be used with base classes too, you just need to define your own custom events. For an example, see redis.RedisClient

    -Source code + +Expand source code +
    """
     This module is for event handling, where any component can listen to certain event notifications
     
    @@ -181,9 +185,11 @@ 

    Functions

    def add_global_listener(handler)
    -
    +
    -Source code + +Expand source code +
    def add_global_listener(handler):
         add_listenter(handler, Any)
    @@ -192,9 +198,11 @@

    Functions

    def add_listenter(handler, *event_types)
    -
    +
    -Source code + +Expand source code +
    def add_listenter(handler, *event_types):
         if not event_types:
             raise ValueError("must specify at least 1 event type/class")
    @@ -207,9 +215,11 @@ 

    Functions

    def handle(event_type)
    -
    +
    -Source code + +Expand source code +
    def handle(event_type):
         def decorator(fun):
             add_listenter(fun, event_type)
    @@ -227,9 +237,11 @@ 

    Functions

    def handle_any(fun)
    -
    +
    -Source code + +Expand source code +
    def handle_any(fun):
         add_global_listener(fun)
     
    @@ -244,9 +256,11 @@ 

    Functions

    def handle_many(*event_types)
    -
    +
    -Source code + +Expand source code +
    def handle_many(*event_types):
         def decorator(fun):
             add_listenter(fun, *event_types)
    @@ -264,9 +278,11 @@ 

    Functions

    def notify(event)
    -
    +
    -Source code + +Expand source code +
    def notify(event):
         event_type = event.__class__
         interested = listeners.get(event_type, []) + listeners[Any]
    @@ -285,24 +301,26 @@ 

    Classes

    class Any -(*args, **kwargs)
    -
    +
    -Source code + +Expand source code +
    class Any:
         pass
    class Handler -(*args, **kwargs)
    -
    +
    -Source code + +Expand source code +
    class Handler:
         def handle(self, ev):
             pass
    @@ -317,9 +335,11 @@

    Methods

    def handle(self, ev)
    -
    +
    -Source code + +Expand source code +
    def handle(self, ev):
         pass
    @@ -367,9 +387,7 @@

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/exceptions/exceptions.html b/docs/api/jumpscale/core/exceptions/exceptions.html index 1f5e4bd77..d12753b9f 100644 --- a/docs/api/jumpscale/core/exceptions/exceptions.html +++ b/docs/api/jumpscale/core/exceptions/exceptions.html @@ -3,15 +3,17 @@ - + jumpscale.core.exceptions.exceptions API documentation - - - - - + + + + + + +
    @@ -38,7 +40,9 @@

    Module jumpscale.core.exceptions.exceptions

  • SSHTimeout
-Source code + +Expand source code +
"""
 Exceptions module provide a carefully picked list of exceptions to be used across the framework
 
@@ -140,9 +144,11 @@ 

Classes

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Base(JSException):
     pass
@@ -158,9 +164,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Bug(JSException):
     pass
@@ -176,9 +184,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Halt(JSException):
     pass
@@ -194,9 +204,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class IO(JSException):
     pass
@@ -212,9 +224,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Input(JSException):
     pass
@@ -230,9 +244,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class JSException(Exception):
     def __init__(self, message, category=None, level=None, context=None):
         super().__init__(message)
@@ -245,20 +261,20 @@

Ancestors

Subclasses

@@ -267,9 +283,11 @@

Subclasses

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class NotFound(JSException):
     pass
@@ -285,9 +303,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class NotImplemented(JSException):
     pass
@@ -303,9 +323,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Operations(JSException):
     pass
@@ -321,9 +343,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Permission(JSException):
     pass
@@ -339,9 +363,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Runtime(JSException):
     pass
@@ -357,9 +383,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class SSH(JSException):
     pass
@@ -375,9 +403,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class SSHTimeout(JSException):
     pass
@@ -393,9 +423,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Timeout(JSException):
     pass
@@ -411,9 +443,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Validation(JSException):
     pass
@@ -429,9 +463,11 @@

Ancestors

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class Value(JSException):
     pass
@@ -512,9 +548,7 @@

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/exceptions/index.html b/docs/api/jumpscale/core/exceptions/index.html index 925f5ce0c..12d7b6120 100644 --- a/docs/api/jumpscale/core/exceptions/index.html +++ b/docs/api/jumpscale/core/exceptions/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.exceptions API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.core.exceptions

-Source code + +Expand source code +
from .exceptions import *
@@ -30,7 +34,7 @@

Sub-modules

jumpscale.core.exceptions.exceptions
-

Exceptions module provide a carefully picked list of exceptions to be used across the framework …

+

Exceptions module provide a carefully picked list of exceptions to be used across the framework …

@@ -61,9 +65,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/executors/command_builder.html b/docs/api/jumpscale/core/executors/command_builder.html index c4b999c26..c05bba0bb 100644 --- a/docs/api/jumpscale/core/executors/command_builder.html +++ b/docs/api/jumpscale/core/executors/command_builder.html @@ -3,15 +3,17 @@ - + jumpscale.core.executors.command_builder API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.core.executors.command_builder
-Source code + +Expand source code +
from functools import wraps
 from subprocess import list2cmdline
 
@@ -65,7 +69,7 @@ 

Functions

def cmd_from_args(func)
-

a decorator to allow passing cmd as a list, with auto-escaping using subprocess.list2cmdline

+

a decorator to allow passing cmd as a list, with auto-escaping using subprocess.list2cmdline

cmd must be the first positional arguments

Args

@@ -74,11 +78,13 @@

Args

Returns

-
function
+
function
a new function
-
+
-Source code + +Expand source code +
def cmd_from_args(func):
     """
     a decorator to allow passing cmd as a list, with auto-escaping using `subprocess.list2cmdline`
@@ -106,9 +112,11 @@ 

Returns

def format_cmd(cmd)
-
+
-Source code + +Expand source code +
def format_cmd(cmd):
     ## code around it to run in tmux.
     pass
@@ -140,9 +148,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/executors/index.html b/docs/api/jumpscale/core/executors/index.html index 82a20b781..c07c57faa 100644 --- a/docs/api/jumpscale/core/executors/index.html +++ b/docs/api/jumpscale/core/executors/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.executors API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.core.executors

-Source code + +Expand source code +
from .local import execute as run_local
 from .remote import execute as run_remote, RemoteExecutor
 from .tmux import execute_in_window as run_tmux
@@ -32,19 +36,19 @@

Sub-modules

jumpscale.core.executors.command_builder
-
+
jumpscale.core.executors.local
-

Local executor allows executing commands within specific env on the local machine. using the executor framework you can retrieve the stdout, stderr, …

+

Local executor allows executing commands within specific env on the local machine. using the executor framework you can retrieve the stdout, stderr, …

jumpscale.core.executors.remote
-

Remote executor allows executing commands within specific env on any machine. using the executor framework you can retrieve the stdout, stderr, and …

+

Remote executor allows executing commands within specific env on any machine. using the executor framework you can retrieve the stdout, stderr, and …

jumpscale.core.executors.tmux
-

tmux module allows manipulation of tmux sessions, pane and launching background commands in it

+

tmux module allows manipulation of tmux sessions, pane and launching background commands in it

@@ -78,9 +82,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/executors/local.html b/docs/api/jumpscale/core/executors/local.html index cd3ca4855..e6997d667 100644 --- a/docs/api/jumpscale/core/executors/local.html +++ b/docs/api/jumpscale/core/executors/local.html @@ -3,15 +3,17 @@ - + jumpscale.core.executors.local API documentation - - - - - + + + + + + +
@@ -37,7 +39,9 @@

Module jumpscale.core.executors.local

<Result cmd='uname' exited=0>

-Source code + +Expand source code +
"""
 Local executor allows executing commands within specific env on the local machine. using the executor framework you can retrieve the stdout, stderr, and the return code as well.
 ```
@@ -187,7 +191,7 @@ 

Functions

def execute(cmd, **command_ctx)
-

execute cmd locally

+

execute cmd locally

Args

cmd
@@ -276,20 +280,22 @@

Args

Raises

-
UnexpectedExit
+
UnexpectedExit
if the command exited nonzero and warn was False.
-
Failure
+
Failure
if the command didn’t even exit cleanly, e.g. if a StreamWatcher raised WatcherError.
-
ThreadException
+
ThreadException
(if the background I/O threads encountered exceptions other than WatcherError).

Returns

-
tuple
+
tuple
return code, stdout, stderr (from invoke.run Result)
-
+
-Source code + +Expand source code +
@cmd_from_args
 def execute(cmd, **command_ctx):
     """execute `cmd` locally
@@ -427,9 +433,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/executors/remote.html b/docs/api/jumpscale/core/executors/remote.html index 240ae934c..0219a3292 100644 --- a/docs/api/jumpscale/core/executors/remote.html +++ b/docs/api/jumpscale/core/executors/remote.html @@ -3,15 +3,17 @@ - + jumpscale.core.executors.remote API documentation - - - - - + + + + + + +
@@ -28,7 +30,9 @@

Module jumpscale.core.executors.remote

JS-NG>

-Source code + +Expand source code +
"""
 Remote executor allows executing commands within specific env on any machine. using the executor framework you can retrieve the stdout, stderr, and the return code as well.
 
@@ -119,23 +123,25 @@ 

Functions

def execute(cmd, command_ctx, connection_ctx)
-

execute a command on a remote context

+

execute a command on a remote context

Args

cmd : str or list
command as a string or an argument list, e.g. "ls -la" or ["ls", "la"]
command_ctx : dict
-
command runner context (the same as local execute())
+
command runner context (the same as local execute())
connection_ctx : dict
context passed to fabric e.g. fabric.Connection(host, user=None, port=None, config=None, gateway=None, forward_agent=None, connect_timeout=None, connect_kwargs=None, inline_ssh_env=None)

Returns

-
tuple
+
tuple
return code, stdout, stderr
-
+
-Source code + +Expand source code +
@cmd_from_args
 def execute(cmd, command_ctx, connection_ctx):
     """
@@ -164,7 +170,7 @@ 

Classes

(**connection_ctx)
-

Remote executor allows executing commands within specific env on the any machine. using the executor framework you can retrieve the stdout, stderr, and the return code as well. +

Remote executor allows executing commands within specific env on the any machine. using the executor framework you can retrieve the stdout, stderr, and the return code as well. e.g with j.core.executors.RemoteExecutor(host="localhost", connect_kwargs={"key_filename":"/home/xmonader/.ssh/id_rsa",}) as c:c.run("hostname")

Args

@@ -172,11 +178,13 @@

Args

connect_kwargs is currently the right place to hand in paramiko connection parameters such as pkey or key_filename. e.g connect_kwargs={"key_filename": "/home/myuser/.ssh/private.key"}

Returns

-
str
+
str
return the result of the executed command
-
+
-Source code + +Expand source code +
class RemoteExecutor:
     """Remote executor allows executing commands within specific env on the any machine. using the executor framework you can retrieve the stdout, stderr, and the return code as well.
     e.g
@@ -223,9 +231,11 @@ 

Instance variables

var connection
-
+
-Source code + +Expand source code +
@property
 def connection(self):
     return fabric.Connection(**self._connection_ctx)
@@ -233,9 +243,11 @@

Instance variables

var sftp
-
+
-Source code + +Expand source code +
@property
 def sftp(self):
     return self.connection.sftp()
@@ -248,7 +260,7 @@

Methods

def run(self, cmd, **command_ctx)
-

execute a command

+

execute a command

Args

cmd : str or list
@@ -256,11 +268,13 @@

Args

Returns

-
tuple
+
tuple
return code, stdout, stderr
-
+
-Source code + +Expand source code +
def run(self, cmd, **command_ctx):
     """
     execute a command
@@ -311,9 +325,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/executors/tmux.html b/docs/api/jumpscale/core/executors/tmux.html index abb6cf53f..6335060e5 100644 --- a/docs/api/jumpscale/core/executors/tmux.html +++ b/docs/api/jumpscale/core/executors/tmux.html @@ -3,15 +3,17 @@ - + jumpscale.core.executors.tmux API documentation - - - - - + + + + + + +
@@ -22,7 +24,9 @@

Module jumpscale.core.executors.tmux

tmux module allows manipulation of tmux sessions, pane and launching background commands in it

-Source code + +Expand source code +
"""tmux module allows manipulation of tmux sessions, pane and launching background commands in it"""
 import libtmux
 from jumpscale.core.logging import export_module_as as logger
@@ -96,16 +100,18 @@ 

Functions

def execute_in_window(cmd, window_name, session_name=None)
-

execute a command in a new tmux window

+

execute a command in a new tmux window

Args

cmd : str or list
command as a string or an argument list, e.g. "ls -la" or ["ls", "la"]
window_name : str
window name
-
+
-Source code + +Expand source code +
@cmd_from_args
 def execute_in_window(cmd, window_name, session_name=None):
     """
@@ -147,9 +153,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/index.html b/docs/api/jumpscale/core/index.html index 33333a96b..ed9bf88fa 100644 --- a/docs/api/jumpscale/core/index.html +++ b/docs/api/jumpscale/core/index.html @@ -3,15 +3,17 @@ - + jumpscale.core API documentation - - - - - + + + + + + +
@@ -26,39 +28,39 @@

Sub-modules

jumpscale.core.application
-
+
jumpscale.core.base
-
+
jumpscale.core.config
-
+
jumpscale.core.db
-
+
jumpscale.core.dirs
-
+
jumpscale.core.events
-

This module is for event handling, where any component can listen to certain event notifications …

+

This module is for event handling, where any component can listen to certain event notifications …

jumpscale.core.exceptions
-
+
jumpscale.core.executors
-
+
jumpscale.core.logging
-
+
@@ -97,9 +99,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/logging/index.html b/docs/api/jumpscale/core/logging/index.html index a2d8c8ccf..e2e522b01 100644 --- a/docs/api/jumpscale/core/logging/index.html +++ b/docs/api/jumpscale/core/logging/index.html @@ -3,15 +3,17 @@ - + jumpscale.core.logging API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.core.logging

-Source code + +Expand source code +
import sys
 
 
@@ -60,7 +64,7 @@ 

Sub-modules

jumpscale.core.logging.logging
-
+
@@ -73,9 +77,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
 
     from jumpscale.loader import j
@@ -136,9 +142,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/core/logging/logging.html b/docs/api/jumpscale/core/logging/logging.html index 2bbef7b90..b4388ebf2 100644 --- a/docs/api/jumpscale/core/logging/logging.html +++ b/docs/api/jumpscale/core/logging/logging.html @@ -3,15 +3,17 @@ - + jumpscale.core.logging.logging API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.core.logging.logging

-Source code + +Expand source code +
import math
 import loguru
 import msgpack
@@ -421,12 +425,13 @@ 

Classes

class LogHandler -(*args, **kwargs)
-

the interface every cutom log handler should implement

+

the interface every cutom log handler should implement

-Source code + +Expand source code +
class LogHandler(ABC):
     """the interface every cutom log handler should implement"""
 
@@ -447,9 +452,11 @@ 

Subclasses

class Logger
-
+
-Source code + +Expand source code +
class Logger:
     def __init__(self):
         self._default_app_name = DEFAULT_APP_NAME
@@ -525,9 +532,11 @@ 

Instance variables

var default_app_name
-
+
-Source code + +Expand source code +
@property
 def default_app_name(self):
     return self._default_app_name
@@ -537,17 +546,16 @@

Instance variables

Methods

-def add_custom_handler(self, name, handler, *args, **kwargs) +def add_custom_handler(self, name: str, handler: LogHandler, *args, **kwargs)
-

Add custom log handler

+

Add custom log handler

Arguments

-
-
handler : LogHandler
-
handler function
-
+

handler (LogHandler): handler function

-Source code + +Expand source code +
def add_custom_handler(self, name: str, handler: LogHandler, *args, **kwargs):
     """
     Add custom log handler
@@ -563,10 +571,12 @@ 

Arguments

def add_handler(self, *args, **kwargs)
-

Add handler to the logger

-

takes the same parameters of loguru.logger.add

+

Add handler to the logger

+

takes the same parameters of loguru.logger.add

-Source code + +Expand source code +
def add_handler(self, *args, **kwargs):
     """
     Add handler to the logger
@@ -577,48 +587,56 @@ 

Arguments

-def critical(self, message, *args, category='', data=None) +def critical(self, message, *args, category: str = '', data: dict = None)
-

Log critical message

+

Log critical message

-Source code + +Expand source code +
def critical(self, message, *args, category: str = "", data: dict = None):
     """Log critical message"""
     self._log("CRITICAL", message, *args, category=category, data=data)
-def debug(self, message, *args, category='', data=None) +def debug(self, message, *args, category: str = '', data: dict = None)
-

Log debug message

+

Log debug message

-Source code + +Expand source code +
def debug(self, message, *args, category: str = "", data: dict = None):
     """Log debug message"""
     self._log("DEBUG", message, *args, category=category, data=data)
-def error(self, message, *args, category='', data=None) +def error(self, message, *args, category: str = '', data: dict = None)
-

Log error message

+

Log error message

-Source code + +Expand source code +
def error(self, message, *args, category: str = "", data: dict = None):
     """Log error message"""
     self._log("ERROR", message, *args, category=category, data=data)
-def exception(self, message, *args, category='', data=None, level=40, exception=None) +def exception(self, message, *args, category: str = '', data: dict = None, level: int = 40, exception: Exception = None)
-

Log exception message

+

Log exception message

-Source code + +Expand source code +
def exception(
     self, message, *args, category: str = "", data: dict = None, level: int = 40, exception: Exception = None
 ):
@@ -627,30 +645,34 @@ 

Arguments

-def info(self, message, *args, category='', data=None) +def info(self, message, *args, category: str = '', data: dict = None)
-

Log info message

+

Log info message

-Source code + +Expand source code +
def info(self, message, *args, category: str = "", data: dict = None):
     """Log info message"""
     self._log("INFO", message, *args, category=category, data=data)
-def remove_handler(self, handler_id) +def remove_handler(self, handler_id: int)
-

Remove loguru handler by id

+

Remove loguru handler by id

The pre-configured handler has the id of 0

Args

handler_id : int
handler id that was returned by add_handler method
-
+
-Source code + +Expand source code +
def remove_handler(self, handler_id: int):
     """
     Remove loguru handler by id
@@ -664,12 +686,14 @@ 

Args

-def warning(self, message, *args, category='', data=None) +def warning(self, message, *args, category: str = '', data: dict = None)
-

Log warning message

+

Log warning message

-Source code + +Expand source code +
def warning(self, message, *args, category: str = "", data: dict = None):
     """Log warning message"""
     self._log("WARNING", message, *args, category=category, data=data)
@@ -681,9 +705,11 @@

Args

class MainLogger
-
+
-Source code + +Expand source code +
class MainLogger(Logger):
     def __init__(self):
         super().__init__()
@@ -823,15 +849,17 @@ 

Methods

def get_app_names(self)
-

Get a set of all registered app names

+

Get a set of all registered app names

If redis is running, it would get them from applications set.

Returns

-
set
+
set
available app names
-
+
-Source code + +Expand source code +
def get_app_names(self):
     """
     Get a set of all registered app names
@@ -855,7 +883,7 @@ 

Returns

def register(self, app_name, module_name=None)
-

Register and bind given module (and sub-modules) logs with a given app name

+

Register and bind given module (and sub-modules) logs with a given app name

Will also add the app to applications set in redis.

If module_name is not passed or empty, it would be the caller module name.

Args

@@ -864,9 +892,11 @@

Args

app name
module_name : str, optional
module name. Defaults to None.
-
+
-Source code + +Expand source code +
def register(self, app_name, module_name=None):
     """
     Register and bind given module (and sub-modules) logs with a given app name
@@ -895,16 +925,18 @@ 

Args

def unregister(self, module_name=None)
-

Unregister a module from log binding with the app name.

+

Unregister a module from log binding with the app name.

Will also remove the app from applications set in redis.

If module_name is not passed or empty, it would be the caller module name.

Args

module_name : str, optional
module name. Defaults to None.
-
+
-Source code + +Expand source code +
def unregister(self, module_name=None):
     """
     Unregister a module from log binding with the app name.
@@ -946,12 +978,14 @@ 

Inherited members

class RedisLogHandler -(max_size=1000, dump=True, dump_dir=None) +(max_size: int = 1000, dump: bool = True, dump_dir: str = None)
-

the interface every cutom log handler should implement

+

the interface every cutom log handler should implement

-Source code + +Expand source code +
class RedisLogHandler(LogHandler):
     def __init__(self, max_size: int = 1000, dump: bool = True, dump_dir: str = None):
         self._max_size = max_size
@@ -1117,9 +1151,11 @@ 

Instance variables

var dump
-
+
-Source code + +Expand source code +
@property
 def dump(self):
     return self._dump
@@ -1127,9 +1163,11 @@

Instance variables

var dump_dir
-
+
-Source code + +Expand source code +
@property
 def dump_dir(self):
     return self._dump_dir
@@ -1137,9 +1175,11 @@

Instance variables

var max_size
-
+
-Source code + +Expand source code +
@property
 def max_size(self):
     return self._max_size
@@ -1149,20 +1189,22 @@

Instance variables

Methods

-def record_get(self, identifier, app_name='init') +def record_get(self, identifier: int, app_name: str = 'init') ‑> dict
-

Get app log record by its identifier

+

Get app log record by its identifier

Arguments

identifier {int} – record identifier app_name {str} – app name

Returns

-
dict
+
dict
requested log record
-
+
-Source code + +Expand source code +
def record_get(self, identifier: int, app_name: str = DEFAULT_APP_NAME) -> dict:
     """Get app log record by its identifier
 
@@ -1192,22 +1234,18 @@ 

Returns

-def records_count(self, app_name='init') +def records_count(self, app_name: str = 'init') ‑> int
-

Gets total number of the records of the app

+

Gets total number of the records of the app

Arguments

-
-
app_name : str
-
app name
-
+

app_name (str): app name

Returns

-
-
inittotal number of the records
-
 
-
+

init – total number of the records

-Source code + +Expand source code +
def records_count(self, app_name: str = DEFAULT_APP_NAME) -> int:
     """Gets total number of the records of the app
 
@@ -1224,17 +1262,16 @@ 

Returns

-def remove_all_records(self, app_name) +def remove_all_records(self, app_name: str)
-

Delete all app's log records

+

Delete all app's log records

Arguments

-
-
app_name : str
-
app name
-
+

app_name (str): app name

-Source code + +Expand source code +
def remove_all_records(self, app_name: str):
     """Delete all app's log records
 
@@ -1249,20 +1286,22 @@ 

Arguments

-def tail(self, app_name='init', limit=None) +def tail(self, app_name: str = 'init', limit: int = None) ‑> 
-

Tail records

+

Tail records

Keyword Arguments: app_name (str): app name. limit (int, optional): max number of record to be returned per page (default: max size)

Yields

-
iter
+
iter
iterator of the requested logs
-
+
-Source code + +Expand source code +
def tail(self, app_name: str = DEFAULT_APP_NAME, limit: int = None) -> iter:
     """Tail records
 
@@ -1343,9 +1382,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/bcdb.html b/docs/api/jumpscale/data/bcdb/bcdb.html index 1360e41f8..c4c381847 100644 --- a/docs/api/jumpscale/data/bcdb/bcdb.html +++ b/docs/api/jumpscale/data/bcdb/bcdb.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.bcdb API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.bcdb

-Source code + +Expand source code +
from redis import Redis
 import json
 from jumpscale.data.bcdb import models as models
@@ -35,7 +39,7 @@ 

Module jumpscale.data.bcdb.bcdb

self.indexer_set = SQLiteIndexSetClient(ns) self.indexer_text = SonicIndexTextClient(ns) self.models = { - + } self.loaded_models = { @@ -49,10 +53,10 @@

Module jumpscale.data.bcdb.bcdb

model = getattr(models, model_name) if isinstance(model, type) and issubclass(model, models.ModelBase): self.models[model._name] = model - + def save_obj(self, model, obj): """Saves the given objects which belongs to model in the db and update the indexes. - + Args: model (ModelObj): The model object that obj belongs to. obj (JSObjBase): The object that will be saved. @@ -71,10 +75,10 @@

Module jumpscale.data.bcdb.bcdb

def model_id_incr(self, model): """Increment the id counter in the model and returns the new id. Used to assign unique id for each created object. - + Args: model (ModelObj): The model object. - + Returns: int: The new unique id """ @@ -82,11 +86,11 @@

Module jumpscale.data.bcdb.bcdb

def get_item_by_id(self, model, id): """Gets the object in the model with the given id. - + Args: model (ModelObj): The model to be searched in. id (int): The object's id. - + Returns: JSObjBase or None: The JSObject with the given id. None if none was found. """ @@ -97,15 +101,15 @@

Module jumpscale.data.bcdb.bcdb

1. It searches in the redis index if key is indexed. 2. Else, It's searched for in the sqlite index if the key is indexed for range search. 3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: JSObjBase or None: The matched object (o: o.key == val). None if none matched. """ @@ -125,16 +129,16 @@

Module jumpscale.data.bcdb.bcdb

def get_range(self, model, key, min, max): """Searches for objects whose key lies between min and max. It tries to search for it in the index. If the key is not indexed it loops through all the objects. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -149,20 +153,20 @@

Module jumpscale.data.bcdb.bcdb

if obj_val >= min and obj_val <= max: result.append(obj) return result - + def get_item_from_index(self, model, key, val): """Search for objects whose key equal val. The key must be indexed for search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key == val) """ @@ -175,17 +179,17 @@

Module jumpscale.data.bcdb.bcdb

def get_item_from_index_set(self, model, key, min, max): """Searches for objects whose key lies between min and max. The key must be indexed for range search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for range search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -194,23 +198,23 @@

Module jumpscale.data.bcdb.bcdb

if not model.schema.props[key].index_key: raise RuntimeError(f"{key} is not indexed.") return [self.get_item_by_id(model, x[0]) for x in self.indexer_set.get(model, key, min, max)] - + def get_item_from_index_text(self, model, key, pattern): """Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index. - + Args: model (Modelobj): The model object in which the pattern is searched. key (str): The model property that the pattern is searched for in. pattern (str): The pattern to be searched for. - + Notes: Currently sonic server matches for some patterns and doesn't for others. Raises: RuntimeError: If the key is not defined in the model. RuntimeError: If the key is not indexed for search - + Returns: list[JSObjBase]: List of matching objects (o: o.key matches pattern). """ @@ -222,13 +226,13 @@

Module jumpscale.data.bcdb.bcdb

def get_model_by_name(self, model_name): """Returns a Model object given its name. - + Args: model_name (str): The name of the model. - + Raises: RuntimeError: Raised when no model exists with the given. - + Returns: ModelObj: The model object. """ @@ -253,9 +257,11 @@

Classes

(ns)
-
+
-Source code + +Expand source code +
class BCDB:
     def __init__(self, ns):
         self.ns = ns
@@ -264,7 +270,7 @@ 

Classes

self.indexer_set = SQLiteIndexSetClient(ns) self.indexer_text = SonicIndexTextClient(ns) self.models = { - + } self.loaded_models = { @@ -278,10 +284,10 @@

Classes

model = getattr(models, model_name) if isinstance(model, type) and issubclass(model, models.ModelBase): self.models[model._name] = model - + def save_obj(self, model, obj): """Saves the given objects which belongs to model in the db and update the indexes. - + Args: model (ModelObj): The model object that obj belongs to. obj (JSObjBase): The object that will be saved. @@ -300,10 +306,10 @@

Classes

def model_id_incr(self, model): """Increment the id counter in the model and returns the new id. Used to assign unique id for each created object. - + Args: model (ModelObj): The model object. - + Returns: int: The new unique id """ @@ -311,11 +317,11 @@

Classes

def get_item_by_id(self, model, id): """Gets the object in the model with the given id. - + Args: model (ModelObj): The model to be searched in. id (int): The object's id. - + Returns: JSObjBase or None: The JSObject with the given id. None if none was found. """ @@ -326,15 +332,15 @@

Classes

1. It searches in the redis index if key is indexed. 2. Else, It's searched for in the sqlite index if the key is indexed for range search. 3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: JSObjBase or None: The matched object (o: o.key == val). None if none matched. """ @@ -354,16 +360,16 @@

Classes

def get_range(self, model, key, min, max): """Searches for objects whose key lies between min and max. It tries to search for it in the index. If the key is not indexed it loops through all the objects. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -378,20 +384,20 @@

Classes

if obj_val >= min and obj_val <= max: result.append(obj) return result - + def get_item_from_index(self, model, key, val): """Search for objects whose key equal val. The key must be indexed for search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key == val) """ @@ -404,17 +410,17 @@

Classes

def get_item_from_index_set(self, model, key, min, max): """Searches for objects whose key lies between min and max. The key must be indexed for range search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for range search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -423,23 +429,23 @@

Classes

if not model.schema.props[key].index_key: raise RuntimeError(f"{key} is not indexed.") return [self.get_item_by_id(model, x[0]) for x in self.indexer_set.get(model, key, min, max)] - + def get_item_from_index_text(self, model, key, pattern): """Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index. - + Args: model (Modelobj): The model object in which the pattern is searched. key (str): The model property that the pattern is searched for in. pattern (str): The pattern to be searched for. - + Notes: Currently sonic server matches for some patterns and doesn't for others. Raises: RuntimeError: If the key is not defined in the model. RuntimeError: If the key is not indexed for search - + Returns: list[JSObjBase]: List of matching objects (o: o.key matches pattern). """ @@ -451,13 +457,13 @@

Classes

def get_model_by_name(self, model_name): """Returns a Model object given its name. - + Args: model_name (str): The name of the model. - + Raises: RuntimeError: Raised when no model exists with the given. - + Returns: ModelObj: The model object. """ @@ -473,9 +479,11 @@

Methods

def detect_models(self)
-

It scans all the models and store its classes for later use.

+

It scans all the models and store its classes for later use.

-Source code + +Expand source code +
def detect_models(self):
     """It scans all the models and store its classes for later use."""
     for model_name in dir(models):
@@ -488,7 +496,7 @@ 

Methods

def get_entry(self, model, key, val)
-

Search for objects whose key equal val. +

Search for objects whose key equal val. 1. It searches in the redis index if key is indexed. 2. Else, It's searched for in the sqlite index if the key is indexed for range search. 3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object.

@@ -503,27 +511,32 @@

Args

Raises

-
RuntimeError
+
RuntimeError
If the key is not a part of the schema.

Returns

-

JSObjBase or None: The matched object (o: o.key == val). None if none matched.

+
+
JSObjBase or None
+
The matched object (o: o.key == val). None if none matched.
+
-Source code + +Expand source code +
def get_entry(self, model, key, val):
     """Search for objects whose key equal val.
     1. It searches in the redis index if key is indexed.
     2. Else, It's searched for in the sqlite index if the key is indexed for range search.
     3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object.
-    
+
     Args:
         model (ModelObj): The model in which the key is searched for.
         key (str): The model property that is checked for.
         val (value): The value.
-    
+
     Raises:
         RuntimeError: If the key is not a part of the schema.
-    
+
     Returns:
         JSObjBase or None: The matched object (o: o.key == val). None if none matched.
     """
@@ -545,7 +558,7 @@ 

Returns

def get_item_by_id(self, model, id)
-

Gets the object in the model with the given id.

+

Gets the object in the model with the given id.

Args

model : ModelObj
@@ -554,16 +567,21 @@

Args

The object's id.

Returns

-

JSObjBase or None: The JSObject with the given id. None if none was found.

+
+
JSObjBase or None
+
The JSObject with the given id. None if none was found.
+
-Source code + +Expand source code +
def get_item_by_id(self, model, id):
     """Gets the object in the model with the given id.
-    
+
     Args:
         model (ModelObj): The model to be searched in.
         id (int): The object's id.
-    
+
     Returns:
         JSObjBase or None: The JSObject with the given id. None if none was found.
     """
@@ -574,7 +592,7 @@ 

Returns

def get_item_from_index(self, model, key, val)
-

Search for objects whose key equal val. The key must be indexed for search.

+

Search for objects whose key equal val. The key must be indexed for search.

Args

model : ModelObj
@@ -586,30 +604,32 @@

Args

Raises

-
RuntimeError
+
RuntimeError
If the key is not a part of the schema.
-
RuntimeError
+
RuntimeError
If the key is not indexed for search.

Returns

-
List[JSObjBase]: A list of matched objects (o: o.key == val)
-
 
-
+
List[JSObjBase]
+
A list of matched objects (o: o.key == val)
+
-Source code + +Expand source code +
def get_item_from_index(self, model, key, val):
     """Search for objects whose key equal val. The key must be indexed for search.
-    
+
     Args:
         model (ModelObj): The model in which the key is searched for.
         key (str): The model property that is checked for.
         val (value): The value.
-    
+
     Raises:
         RuntimeError: If the key is not a part of the schema.
         RuntimeError: If the key is not indexed for search.
-    
+
     Returns:
         List[JSObjBase]: A list of matched objects (o: o.key == val)
     """
@@ -625,7 +645,7 @@ 

Returns

def get_item_from_index_set(self, model, key, min, max)
-

Searches for objects whose key lies between min and max. The key must be indexed for range search.

+

Searches for objects whose key lies between min and max. The key must be indexed for range search.

Args

model : ModelObj
@@ -639,31 +659,33 @@

Args

Raises

-
RuntimeError
+
RuntimeError
If the key is not a part of the schema.
-
RuntimeError
+
RuntimeError
If the key is not indexed for range search.

Returns

-
List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max)
-
 
-
+
List[JSObjBase]
+
A list of matched objects (o: o.key >= min and o.key <= max)
+
-Source code + +Expand source code +
def get_item_from_index_set(self, model, key, min, max):
     """Searches for objects whose key lies between min and max. The key must be indexed for range search.
-    
+
     Args:
         model (ModelObj): The model in which the key is searched for.
         key (str): The model property that is checked for.
         min (value): The minimum.
         max (value): The maximum.
-    
+
     Raises:
         RuntimeError: If the key is not a part of the schema.
         RuntimeError: If the key is not indexed for range search.
-    
+
     Returns:
         List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max)
     """
@@ -678,7 +700,7 @@ 

Returns

def get_item_from_index_text(self, model, key, pattern)
-

Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index.

+

Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index.

Args

model : Modelobj
@@ -692,30 +714,35 @@

Notes

Currently sonic server matches for some patterns and doesn't for others.

Raises

-
RuntimeError
+
RuntimeError
If the key is not defined in the model.
-
RuntimeError
+
RuntimeError
If the key is not indexed for search

Returns

-

list[JSObjBase]: List of matching objects (o: o.key matches pattern).

+
+
list[JSObjBase]
+
List of matching objects (o: o.key matches pattern).
+
-Source code + +Expand source code +
def get_item_from_index_text(self, model, key, pattern):
     """Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index.
-    
+
     Args:
         model (Modelobj): The model object in which the pattern is searched.
         key (str): The model property that the pattern is searched for in.
         pattern (str): The pattern to be searched for.
-    
+
     Notes:
         Currently sonic server matches for some patterns and doesn't for others.
 
     Raises:
         RuntimeError: If the key is not defined in the model.
         RuntimeError: If the key is not indexed for search
-    
+
     Returns:
         list[JSObjBase]: List of matching objects (o: o.key matches pattern).
     """
@@ -730,7 +757,7 @@ 

Returns

def get_model_by_name(self, model_name)
-

Returns a Model object given its name.

+

Returns a Model object given its name.

Args

model_name : str
@@ -738,25 +765,27 @@

Args

Raises

-
RuntimeError
+
RuntimeError
Raised when no model exists with the given.

Returns

-
ModelObj
+
ModelObj
The model object.
-
+
-Source code + +Expand source code +
def get_model_by_name(self, model_name):
     """Returns a Model object given its name.
-    
+
     Args:
         model_name (str): The name of the model.
-    
+
     Raises:
         RuntimeError: Raised when no model exists with the given.
-    
+
     Returns:
         ModelObj: The model object.
     """
@@ -771,7 +800,7 @@ 

Returns

def get_range(self, model, key, min, max)
-

Searches for objects whose key lies between min and max. +

Searches for objects whose key lies between min and max. It tries to search for it in the index. If the key is not indexed it loops through all the objects.

Args

@@ -786,29 +815,31 @@

Args

Raises

-
RuntimeError
+
RuntimeError
If the key is not a part of the schema.

Returns

-
List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max)
-
 
-
+
List[JSObjBase]
+
A list of matched objects (o: o.key >= min and o.key <= max)
+
-Source code + +Expand source code +
def get_range(self, model, key, min, max):
     """Searches for objects whose key lies between min and max.
     It tries to search for it in the index. If the key is not indexed it loops through all the objects.
-    
+
     Args:
         model (ModelObj): The model in which the key is searched for.
         key (str): The model property that is checked for.
         min (value): The minimum.
         max (value): The maximum.
-    
+
     Raises:
         RuntimeError: If the key is not a part of the schema.
-    
+
     Returns:
         List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max)
     """
@@ -829,7 +860,7 @@ 

Returns

def model_id_incr(self, model)
-

Increment the id counter in the model and returns the new id. +

Increment the id counter in the model and returns the new id. Used to assign unique id for each created object.

Args

@@ -838,18 +869,20 @@

Args

Returns

-
int
+
int
The new unique id
-
+
-Source code + +Expand source code +
def model_id_incr(self, model):
     """Increment the id counter in the model and returns the new id.
     Used to assign unique id for each created object.
-    
+
     Args:
         model (ModelObj): The model object.
-    
+
     Returns:
         int: The new unique id
     """
@@ -860,19 +893,21 @@ 

Returns

def save_obj(self, model, obj)
-

Saves the given objects which belongs to model in the db and update the indexes.

+

Saves the given objects which belongs to model in the db and update the indexes.

Args

model : ModelObj
The model object that obj belongs to.
obj : JSObjBase
The object that will be saved.
-
+
-Source code + +Expand source code +
def save_obj(self, model, obj):
     """Saves the given objects which belongs to model in the db and update the indexes.
-    
+
     Args:
         model (ModelObj): The model object that obj belongs to.
         obj (JSObjBase): The object that will be saved.
@@ -928,9 +963,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/clients.html b/docs/api/jumpscale/data/bcdb/clients.html index caf3d6319..bbdb9fdcf 100644 --- a/docs/api/jumpscale/data/bcdb/clients.html +++ b/docs/api/jumpscale/data/bcdb/clients.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.clients API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.clients

-Source code + +Expand source code +
from .interfaces import *
 from redis import Redis
 import json
@@ -163,12 +167,13 @@ 

Classes

class JSONSerializer -(*args, **kwargs)
-
+
-Source code + +Expand source code +
class JSONSerializer(SerializerInterface):
     def loads(self, model, s):
         return model.load_obj_from_dict(json.loads(s))
@@ -186,9 +191,11 @@ 

Methods

def dumps(self, model, data)
-
+
-Source code + +Expand source code +
def dumps(self, model, data):
     return json.dumps(model.get_dict(data))
@@ -197,9 +204,11 @@

Methods

def loads(self, model, s)
-
+
-Source code + +Expand source code +
def loads(self, model, s):
     return model.load_obj_from_dict(json.loads(s))
@@ -211,9 +220,11 @@

Methods

(bcdb_namespace, host='localhost', port=6379)
-
+
-Source code + +Expand source code +
class RedisIndexClient(IndexInterface):
     def __init__(self, bcdb_namespace, host="localhost", port=6379):
         self.redis_client = Redis(host=host, port=port)
@@ -238,9 +249,11 @@ 

Methods

def get(self, model, index_prop, index_value)
-
+
-Source code + +Expand source code +
def get(self, model, index_prop, index_value):
     res = self.redis_client.get(f"{self.bcdb_namespace}.indexer.{model.name}.{index_prop}://{index_value}")
     return int(res) if res else None
@@ -250,9 +263,11 @@

Methods

def set(self, model, index_prop, index_value, obj_id, old_value=None)
-
+
-Source code + +Expand source code +
def set(self, model, index_prop, index_value, obj_id, old_value=None):
     if old_value:
         self.redis_client.delete(f"{self.bcdb_namespace}.indexer.{model.name}.{index_prop}://{old_value}")
@@ -266,9 +281,11 @@ 

Methods

(bcdb_namespace, host='localhost', port=6379, serializer=None)
-
+
-Source code + +Expand source code +
class RedisStorageClient(StorageInterface):
     def __init__(self, bcdb_namespace, host="localhost", port=6379, serializer=None):
         self.redis_client = Redis(host=host, port=port)
@@ -307,9 +324,11 @@ 

Methods

def get(self, model, obj_id)
-
+
-Source code + +Expand source code +
def get(self, model, obj_id):
     obj_str = self.redis_client.get(f"{self.bcdb_namespace}.{model.name}://{obj_id}")
     return self.serializer.loads(model, obj_str) if obj_str else None
@@ -319,9 +338,11 @@

Methods

def get_keys_in_model(self, model)
-
+
-Source code + +Expand source code +
def get_keys_in_model(self, model):
     pattern = f"{self.bcdb_namespace}.{model.name}://*"
     result = []
@@ -337,9 +358,11 @@ 

Methods

def incr_id(self, model)
-
+
-Source code + +Expand source code +
def incr_id(self, model):
     return self.redis_client.incr(f"{self.bcdb_namespace}.{model.name}.lastid")
@@ -348,9 +371,11 @@

Methods

def set(self, model, obj_id, value)
-
+
-Source code + +Expand source code +
def set(self, model, obj_id, value):
     return self.redis_client.set(
         f"{self.bcdb_namespace}.{model.name}://{obj_id}", self.serializer.dumps(model, value)
@@ -364,9 +389,11 @@ 

Methods

(bcdb_namespace)
-
+
-Source code + +Expand source code +
class SQLiteIndexSetClient(IndexSetInterface):
     def __init__(self, bcdb_namespace):
         self.bcdb_namespace = bcdb_namespace
@@ -428,9 +455,11 @@ 

Methods

def get(self, model, index_prop, min, max)
-
+
-Source code + +Expand source code +
def get(self, model, index_prop, min, max):
     self._create_if_not_exists(model)
     conn = sqlite3.connect(f"{self.bcdb_namespace}_index.db")
@@ -446,9 +475,11 @@ 

Methods

def set(self, model, obj)
-
+
-Source code + +Expand source code +
def set(self, model, obj):
     self._create_if_not_exists(model)
     conn = sqlite3.connect(f"{self.bcdb_namespace}_index.db")
@@ -473,9 +504,11 @@ 

Methods

(bcdb_namespace)
-
+
-Source code + +Expand source code +
class SonicIndexTextClient(IndexTextInterface):
     def __init__(self, bcdb_namespace):
         self.bcdb_namespace = bcdb_namespace
@@ -504,9 +537,11 @@ 

Methods

def get(self, model, index_prop, pattern)
-
+
-Source code + +Expand source code +
def get(self, model, index_prop, pattern):
     return self.sonic_client.query(self.bcdb_namespace, f"{model.name}_{index_prop}", pattern)
@@ -515,9 +550,11 @@

Methods

def set(self, model, obj)
-
+
-Source code + +Expand source code +
def set(self, model, obj):
     for prop in model.schema.props.values():
         if prop.index_text:
@@ -587,9 +624,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/dumpsql.html b/docs/api/jumpscale/data/bcdb/dumpsql.html index 09940c3b3..81693cd81 100644 --- a/docs/api/jumpscale/data/bcdb/dumpsql.html +++ b/docs/api/jumpscale/data/bcdb/dumpsql.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.dumpsql API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.dumpsql

-Source code + +Expand source code +
def main():
     import sqlite3
     from pprint import pprint
@@ -49,9 +53,11 @@ 

Functions

def main()
-
+
-Source code + +Expand source code +
def main():
     import sqlite3
     from pprint import pprint
@@ -89,9 +95,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/flush.html b/docs/api/jumpscale/data/bcdb/flush.html index 8cb69c6a3..68faff70d 100644 --- a/docs/api/jumpscale/data/bcdb/flush.html +++ b/docs/api/jumpscale/data/bcdb/flush.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.flush API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.flush

-Source code + +Expand source code +
def main():
     import os
     from jumpscale.loader import j
@@ -56,9 +60,11 @@ 

Functions

def main()
-
+
-Source code + +Expand source code +
def main():
     import os
     from jumpscale.loader import j
@@ -103,9 +109,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/index.html b/docs/api/jumpscale/data/bcdb/index.html index 98d904405..350c2fc9a 100644 --- a/docs/api/jumpscale/data/bcdb/index.html +++ b/docs/api/jumpscale/data/bcdb/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb

-Source code + +Expand source code +
from .bcdb import *
@@ -30,27 +34,27 @@

Sub-modules

jumpscale.data.bcdb.bcdb
-
+
jumpscale.data.bcdb.clients
-
+
jumpscale.data.bcdb.dumpsql
-
+
jumpscale.data.bcdb.flush
-
+
jumpscale.data.bcdb.interfaces
-
+
jumpscale.data.bcdb.models
-
+

@@ -86,9 +90,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/interfaces.html b/docs/api/jumpscale/data/bcdb/interfaces.html index 1042bb423..15a40eec2 100644 --- a/docs/api/jumpscale/data/bcdb/interfaces.html +++ b/docs/api/jumpscale/data/bcdb/interfaces.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.interfaces API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.interfaces

-Source code + +Expand source code +
class StorageInterface:
     def get(self, model, obj_id):
         pass
@@ -63,7 +67,7 @@ 

Module jumpscale.data.bcdb.interfaces

pass def get(self, model, index_prop, pattern): - pass + pass class SerializerInterface: def loads(self, model, s): @@ -87,9 +91,11 @@

Classes

(bcdb_namespace)
-
+
-Source code + +Expand source code +
class IndexInterface:
     def __init__(self, bcdb_namespace):
         pass
@@ -110,9 +116,11 @@ 

Methods

def get(self, model, index_prop, index_value)
-
+
-Source code + +Expand source code +
def get(self, model, index_prop, index_value):
     pass
@@ -121,9 +129,11 @@

Methods

def set(self, model, index_prop, index_value, obj_id, old_value=None)
-
+
-Source code + +Expand source code +
def set(self, model, index_prop, index_value, obj_id, old_value=None):
     pass
@@ -135,9 +145,11 @@

Methods

(bcdb_namespace)
-
+
-Source code + +Expand source code +
class IndexSetInterface:
     def __init__(self, bcdb_namespace):
         pass
@@ -158,9 +170,11 @@ 

Methods

def get(self, model, index_prop, min, max)
-
+
-Source code + +Expand source code +
def get(self, model, index_prop, min, max):
     pass
@@ -169,9 +183,11 @@

Methods

def set(self, model, obj)
-
+
-Source code + +Expand source code +
def set(self, model, obj):
     pass
@@ -183,9 +199,11 @@

Methods

(bcdb_namespace)
-
+
-Source code + +Expand source code +
class IndexTextInterface:
     def __init__(self, bcdb_namespace):
         pass
@@ -206,9 +224,11 @@ 

Methods

def get(self, model, index_prop, pattern)
-
+
-Source code + +Expand source code +
def get(self, model, index_prop, pattern):
     pass 
@@ -217,9 +237,11 @@

Methods

def set(self, model, obj)
-
+
-Source code + +Expand source code +
def set(self, model, obj):
    pass
@@ -228,12 +250,13 @@

Methods

class SerializerInterface -(*args, **kwargs)
-
+
-Source code + +Expand source code +
class SerializerInterface:
     def loads(self, model, s):
         pass
@@ -251,9 +274,11 @@ 

Methods

def dumps(self, model, data)
-
+
-Source code + +Expand source code +
def dumps(self, model, data):
     pass
@@ -262,9 +287,11 @@

Methods

def loads(self, model, s)
-
+
-Source code + +Expand source code +
def loads(self, model, s):
     pass
@@ -273,12 +300,13 @@

Methods

class StorageInterface -(*args, **kwargs)
-
+
-Source code + +Expand source code +
class StorageInterface:
     def get(self, model, obj_id):
         pass
@@ -302,9 +330,11 @@ 

Methods

def get(self, model, obj_id)
-
+
-Source code + +Expand source code +
def get(self, model, obj_id):
     pass
@@ -313,9 +343,11 @@

Methods

def get_keys_in_model(self, model)
-
+
-Source code + +Expand source code +
def get_keys_in_model(self, model):
     pass
@@ -324,9 +356,11 @@

Methods

def incr_id(self, model)
-
+
-Source code + +Expand source code +
def incr_id(self, model):
     pass
@@ -335,9 +369,11 @@

Methods

def set(self, model, obj_id, value)
-
+
-Source code + +Expand source code +
def set(self, model, obj_id, value):
     pass
@@ -403,9 +439,7 @@

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/base.html b/docs/api/jumpscale/data/bcdb/models/base.html index dbf6b937c..4d97451fa 100644 --- a/docs/api/jumpscale/data/bcdb/models/base.html +++ b/docs/api/jumpscale/data/bcdb/models/base.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models.base API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models.base

-Source code + +Expand source code +
from jumpscale.loader import j
 from jumpscale.data.schema import Property
 from jumpscale.data.types import Integer, JSObject
@@ -224,9 +228,11 @@ 

Classes

(model)
-
+
-Source code + +Expand source code +
class JSObjBase:
     def __init__(self, model):
         self.model = model
@@ -246,9 +252,11 @@ 

Methods

def get_dict(self)
-
+
-Source code + +Expand source code +
def get_dict(self):
     return self.model.get_dict(self)
@@ -257,9 +265,11 @@

Methods

def save(self)
-
+
-Source code + +Expand source code +
def save(self):
     return self.model.save_obj(self)
@@ -271,9 +281,11 @@

Methods

(bcdb)
-
+
-Source code + +Expand source code +
class ModelBase:
     _schema = ""
     _name = ""
@@ -443,13 +455,13 @@ 

Methods

Subclasses

Methods

@@ -457,7 +469,7 @@

Methods

def create_obj(self, data)
-

Create a new object and assign a new id to it with the given data dict. +

Create a new object and assign a new id to it with the given data dict. Missing props are defaultly initialized.

Args

@@ -466,11 +478,13 @@

Args

Returns

-
JSObjBase
+
JSObjBase
The newly created
-
+
-Source code + +Expand source code +
def create_obj(self, data):
     """Create a new object and assign a new id to it with the given data dict.
     Missing props are defaultly initialized.
@@ -491,7 +505,7 @@ 

Returns

def get_by(self, key, value)
-

Search for objects whose key equal value. +

Search for objects whose key equal value. 1. It searches in the redis index if key is indexed. 2. Else, It's searched for in the sqlite index if the key is indexed for range search. 3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object.

@@ -504,13 +518,18 @@

Args

Raises

-
RuntimeError
+
RuntimeError
If the key is not a part of the schema.

Returns

-

JSObjBase or None: The matched object (o: o.key == val). None if none matched.

+
+
JSObjBase or None
+
The matched object (o: o.key == val). None if none matched.
+
-Source code + +Expand source code +
def get_by(self, key, value):
     """Search for objects whose key equal value.
     1. It searches in the redis index if key is indexed.
@@ -534,19 +553,21 @@ 

Returns

def get_dict(self, obj)
-

Extracts a dict with all attributes from obj.

+

Extracts a dict with all attributes from obj.

Args

-
obj : JSObjBase
+
obj : JSObjBase
The object that the data is extracted from.

Returns

-
dict
+
dict
The data dict.
-
+
-Source code + +Expand source code +
def get_dict(self, obj):
     """Extracts a dict with all attributes from obj.
 
@@ -569,7 +590,7 @@ 

Returns

def get_pattern(self, key, pattern)
-

Searches for objects whose key matches the given pattern in this model. The key must be registered in the text index.

+

Searches for objects whose key matches the given pattern in this model. The key must be registered in the text index.

Args

key : str
@@ -581,15 +602,20 @@

Notes

Currently sonic server matches for some patterns and doesn't for others.

Raises

-
RuntimeError
+
RuntimeError
If the key is not defined in the model.
-
RuntimeError
+
RuntimeError
If the key is not indexed for search

Returns

-

list[JSObjBase]: List of matching objects (o: o.key matches pattern).

+
+
list[JSObjBase]
+
List of matching objects (o: o.key matches pattern).
+
-Source code + +Expand source code +
def get_pattern(self, key, pattern):
     """Searches for objects whose key matches the given pattern in this model. The key must be registered in the text index.
 
@@ -614,9 +640,11 @@ 

Returns

def get_range(self, key, min, max)
-
+
-Source code + +Expand source code +
def get_range(self, key, min, max):
     return self.bcdb.get_item_from_index_set(self, key, min, max)
@@ -625,7 +653,7 @@

Returns

def load_obj_from_dict(self, d)
-

Creates a new object with its data extracted from the d.

+

Creates a new object with its data extracted from the d.

Args

d : dict
@@ -633,11 +661,13 @@

Args

Returns

-
JSObjBase
+
JSObjBase
The newly created object.
-
+
-Source code + +Expand source code +
def load_obj_from_dict(self, d):
     """Creates a new object with its data extracted from the d.
 
@@ -656,14 +686,16 @@ 

Returns

def save_obj(self, obj)
-

Saves the object to the db. It forwards the call to the bcdb client.

+

Saves the object to the db. It forwards the call to the bcdb client.

Args

-
obj : JSObjBase
+
obj : JSObjBase
The object to be saved.
-
+
-Source code + +Expand source code +
def save_obj(self, obj):
     """Saves the object to the db. It forwards the call to the bcdb client.
 
@@ -678,11 +710,11 @@ 

Args

def set_from_dict(self, o, d)
-

Sets the attributes in the object o to the data from the dict d. +

Sets the attributes in the object o to the data from the dict d. Default values is used if it's not present in d.

Args

-
o : JSObjBase
+
o : JSObjBase
The object that will be set.
d : dict
The dict containing the object new data.
@@ -692,16 +724,18 @@

Notes

It was assumed to receive strings convertable to the defined property.

Raises

-
ValueError
+
ValueError
If a value in the dict doesn't conform to the type checking rules imposed by the model's schema.

Returns

-
JSObjBase
+
JSObjBase
The JSObject after setting its data from the dict d.
-
+
-Source code + +Expand source code +
def set_from_dict(self, o, d):
     """Sets the attributes in the object o to the data from the dict d.
     Default values is used if it's not present in d.
@@ -779,9 +813,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/db_model.html b/docs/api/jumpscale/data/bcdb/models/db_model.html index 9268fcfd1..bed186dd2 100644 --- a/docs/api/jumpscale/data/bcdb/models/db_model.html +++ b/docs/api/jumpscale/data/bcdb/models/db_model.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models.db_model API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models.db_model

-Source code + +Expand source code +
from .base import ModelBase
 
 class DBModel(ModelBase):
@@ -47,9 +51,11 @@ 

Classes

(bcdb)
-
+
-Source code + +Expand source code +
class DBModel(ModelBase):
     _schema = """
     @url = db
@@ -102,9 +108,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/emplyee_model.html b/docs/api/jumpscale/data/bcdb/models/emplyee_model.html index 4d8d739fa..9c2fc5fef 100644 --- a/docs/api/jumpscale/data/bcdb/models/emplyee_model.html +++ b/docs/api/jumpscale/data/bcdb/models/emplyee_model.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models.emplyee_model API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models.emplyee_model
-Source code + +Expand source code +
from .base import ModelBase
 
 class EmployeeModel(ModelBase):
@@ -48,9 +52,11 @@ 

Classes

(bcdb)
-
+
-Source code + +Expand source code +
class EmployeeModel(ModelBase):
     _schema = """
     @url = employee
@@ -104,9 +110,7 @@ 

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/index.html b/docs/api/jumpscale/data/bcdb/models/index.html index 8f680dca4..0d4f460b7 100644 --- a/docs/api/jumpscale/data/bcdb/models/index.html +++ b/docs/api/jumpscale/data/bcdb/models/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models

-Source code + +Expand source code +
from .model_model import ModelBase
 import os
 import importlib
@@ -44,35 +48,35 @@ 

Sub-modules

jumpscale.data.bcdb.models.base
-
+
jumpscale.data.bcdb.models.db_model
-
+
jumpscale.data.bcdb.models.emplyee_model
-
+
jumpscale.data.bcdb.models.model_model
-
+
jumpscale.data.bcdb.models.post_model
-
+
jumpscale.data.bcdb.models.proj_model
-
+
jumpscale.data.bcdb.models.test_model
-
+
jumpscale.data.bcdb.models.user_model
-
+
@@ -85,9 +89,11 @@

Functions

def add_model(file_name)
-
+
-Source code + +Expand source code +
def add_model(file_name):
     m = importlib.import_module("." + file_name[:-3], "jumpscale.data.bcdb.models")
     for attr in dir(m):
@@ -133,9 +139,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/model_model.html b/docs/api/jumpscale/data/bcdb/models/model_model.html index d3ee67839..32ee6b4b0 100644 --- a/docs/api/jumpscale/data/bcdb/models/model_model.html +++ b/docs/api/jumpscale/data/bcdb/models/model_model.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models.model_model API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models.model_model

-Source code + +Expand source code +
from jumpscale.loader import j
 
 from .base import ModelBase, JSObjBase
@@ -50,9 +54,11 @@ 

Classes

(bcdb)
-
+
-Source code + +Expand source code +
class ModelModel(ModelBase):
     _schema = """
     @url = model
@@ -105,9 +111,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/post_model.html b/docs/api/jumpscale/data/bcdb/models/post_model.html index 48eda0d2d..06bc8d79f 100644 --- a/docs/api/jumpscale/data/bcdb/models/post_model.html +++ b/docs/api/jumpscale/data/bcdb/models/post_model.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models.post_model API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models.post_model

-Source code + +Expand source code +
from .base import ModelBase
 
 class QuoteModel(ModelBase):
@@ -47,9 +51,11 @@ 

Classes

(bcdb)
-
+
-Source code + +Expand source code +
class QuoteModel(ModelBase):
     _schema = """
     @url = quote
@@ -102,9 +108,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/proj_model.html b/docs/api/jumpscale/data/bcdb/models/proj_model.html index e97ba4d99..1bbc85440 100644 --- a/docs/api/jumpscale/data/bcdb/models/proj_model.html +++ b/docs/api/jumpscale/data/bcdb/models/proj_model.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models.proj_model API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models.proj_model

-Source code + +Expand source code +
from .base import ModelBase
 
 class ProjModel(ModelBase):
@@ -48,9 +52,11 @@ 

Classes

(bcdb)
-
+
-Source code + +Expand source code +
class ProjModel(ModelBase):
     _schema = """
     @url = proj
@@ -104,9 +110,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/test_model.html b/docs/api/jumpscale/data/bcdb/models/test_model.html index 641400483..c72769c64 100644 --- a/docs/api/jumpscale/data/bcdb/models/test_model.html +++ b/docs/api/jumpscale/data/bcdb/models/test_model.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models.test_model API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models.test_model

-Source code + +Expand source code +
from .base import ModelBase
 
 class TestModel(ModelBase):
@@ -46,9 +50,11 @@ 

Classes

(bcdb)
-
+
-Source code + +Expand source code +
class TestModel(ModelBase):
     _schema = """
     @url = test
@@ -100,9 +106,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/bcdb/models/user_model.html b/docs/api/jumpscale/data/bcdb/models/user_model.html index 2031902b8..4e47391c0 100644 --- a/docs/api/jumpscale/data/bcdb/models/user_model.html +++ b/docs/api/jumpscale/data/bcdb/models/user_model.html @@ -3,15 +3,17 @@ - + jumpscale.data.bcdb.models.user_model API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.bcdb.models.user_model

-Source code + +Expand source code +
from .base import ModelBase
 
 class UserModel(ModelBase):
@@ -47,9 +51,11 @@ 

Classes

(bcdb)
-
+
-Source code + +Expand source code +
class UserModel(ModelBase):
     _schema = """
     @url = user
@@ -102,9 +108,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/cache/index.html b/docs/api/jumpscale/data/cache/index.html index b0f10d0ec..d404e754e 100644 --- a/docs/api/jumpscale/data/cache/index.html +++ b/docs/api/jumpscale/data/cache/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.cache API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.cache

-Source code + +Expand source code +
from functools import lru_cache
@@ -49,9 +53,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/encryption/exceptions.html b/docs/api/jumpscale/data/encryption/exceptions.html index 45cda2a58..b6623ad09 100644 --- a/docs/api/jumpscale/data/encryption/exceptions.html +++ b/docs/api/jumpscale/data/encryption/exceptions.html @@ -3,15 +3,17 @@ - + jumpscale.data.encryption.exceptions API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.encryption.exceptions

-Source code + +Expand source code +
from jumpscale.core.exceptions import JSException
 
 
@@ -43,9 +47,11 @@ 

Classes

(message, category=None, level=None, context=None)
-

Common base class for all non-exit exceptions.

+

Common base class for all non-exit exceptions.

-Source code + +Expand source code +
class FailedChecksumError(JSException):
     pass
@@ -81,9 +87,7 @@

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/encryption/index.html b/docs/api/jumpscale/data/encryption/index.html index d4fb43c17..6843c281d 100644 --- a/docs/api/jumpscale/data/encryption/index.html +++ b/docs/api/jumpscale/data/encryption/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.encryption API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.encryption

-Source code + +Expand source code +
from .mnemonic import key_to_mnemonic, mnemonic_to_key, generate_mnemonic
@@ -30,15 +34,15 @@

Sub-modules

jumpscale.data.encryption.exceptions
-
+
jumpscale.data.encryption.mnemonic
-
+
jumpscale.data.encryption.wordlist
-
+

@@ -71,9 +75,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/encryption/mnemonic.html b/docs/api/jumpscale/data/encryption/mnemonic.html index 711d53e88..4df6dbfe8 100644 --- a/docs/api/jumpscale/data/encryption/mnemonic.html +++ b/docs/api/jumpscale/data/encryption/mnemonic.html @@ -3,15 +3,17 @@ - + jumpscale.data.encryption.mnemonic API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.encryption.mnemonic

-Source code + +Expand source code +
import os
 import hashlib
 import binascii
@@ -160,9 +164,11 @@ 

Functions

def binary_search(a, x, lo=0, hi=None)
-
+
-Source code + +Expand source code +
def binary_search(a, x, lo=0, hi=None):  # can't use a to specify default for hi
     hi = hi if hi is not None else len(a)  # hi defaults to len(a)
     pos = bisect.bisect_left(a, x, lo, hi)  # find insertion position
@@ -173,7 +179,7 @@ 

Functions

def entropy(strength=256)
-

Returns random bytes of length strengh/8.

+

Returns random bytes of length strengh/8.

Args

strength : int, optional
@@ -181,11 +187,13 @@

Args

Returns

-
bytes
+
bytes
The generated bytes.
-
+
-Source code + +Expand source code +
def entropy(strength=256):
     """Returns random bytes of length strengh/8.
 
@@ -199,24 +207,26 @@ 

Returns

-def generate_mnemonic(strength=256, wordlist=['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual', 'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance', 'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent', 'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album', 'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone', 'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among', 'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry', 'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique', 'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april', 'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor', 'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact', 'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume', 'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction', 'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado', 'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis', 'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball', 'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base', 'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become', 'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt', 'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle', 'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black', 'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood', 'blossom', 'blouse', 'blue', 'blur', 'blush', 'board', 'boat', 'body', 'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring', 'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain', 'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief', 'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother', 'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb', 'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus', 'business', 'busy', 'butter', 'buyer', 'buzz', 'cabbage', 'cabin', 'cable', 'cactus', 'cage', 'cake', 'call', 'calm', 'camera', 'camp', 'can', 'canal', 'cancel', 'candy', 'cannon', 'canoe', 'canvas', 'canyon', 'capable', 'capital', 'captain', 'car', 'carbon', 'card', 'cargo', 'carpet', 'carry', 'cart', 'case', 'cash', 'casino', 'castle', 'casual', 'cat', 'catalog', 'catch', 'category', 'cattle', 'caught', 'cause', 'caution', 'cave', 'ceiling', 'celery', 'cement', 'census', 'century', 'cereal', 'certain', 'chair', 'chalk', 'champion', 'change', 'chaos', 'chapter', 'charge', 'chase', 'chat', 'cheap', 'check', 'cheese', 'chef', 'cherry', 'chest', 'chicken', 'chief', 'child', 'chimney', 'choice', 'choose', 'chronic', 'chuckle', 'chunk', 'churn', 'cigar', 'cinnamon', 'circle', 'citizen', 'city', 'civil', 'claim', 'clap', 'clarify', 'claw', 'clay', 'clean', 'clerk', 'clever', 'click', 'client', 'cliff', 'climb', 'clinic', 'clip', 'clock', 'clog', 'close', 'cloth', 'cloud', 'clown', 'club', 'clump', 'cluster', 'clutch', 'coach', 'coast', 'coconut', 'code', 'coffee', 'coil', 'coin', 'collect', 'color', 'column', 'combine', 'come', 'comfort', 'comic', 'common', 'company', 'concert', 'conduct', 'confirm', 'congress', 'connect', 'consider', 'control', 'convince', 'cook', 'cool', 'copper', 'copy', 'coral', 'core', 'corn', 'correct', 'cost', 'cotton', 'couch', 'country', 'couple', 'course', 'cousin', 'cover', 'coyote', 'crack', 'cradle', 'craft', 'cram', 'crane', 'crash', 'crater', 'crawl', 'crazy', 'cream', 'credit', 'creek', 'crew', 'cricket', 'crime', 'crisp', 'critic', 'crop', 'cross', 'crouch', 'crowd', 'crucial', 'cruel', 'cruise', 'crumble', 'crunch', 'crush', 'cry', 'crystal', 'cube', 'culture', 'cup', 'cupboard', 'curious', 'current', 'curtain', 'curve', 'cushion', 'custom', 'cute', 'cycle', 'dad', 'damage', 'damp', 'dance', 'danger', 'daring', 'dash', 'daughter', 'dawn', 'day', 'deal', 'debate', 'debris', 'decade', 'december', 'decide', 'decline', 'decorate', 'decrease', 'deer', 'defense', 'define', 'defy', 'degree', 'delay', 'deliver', 'demand', 'demise', 'denial', 'dentist', 'deny', 'depart', 'depend', 'deposit', 'depth', 'deputy', 'derive', 'describe', 'desert', 'design', 'desk', 'despair', 'destroy', 'detail', 'detect', 'develop', 'device', 'devote', 'diagram', 'dial', 'diamond', 'diary', 'dice', 'diesel', 'diet', 'differ', 'digital', 'dignity', 'dilemma', 'dinner', 'dinosaur', 'direct', 'dirt', 'disagree', 'discover', 'disease', 'dish', 'dismiss', 'disorder', 'display', 'distance', 'divert', 'divide', 'divorce', 'dizzy', 'doctor', 'document', 'dog', 'doll', 'dolphin', 'domain', 'donate', 'donkey', 'donor', 'door', 'dose', 'double', 'dove', 'draft', 'dragon', 'drama', 'drastic', 'draw', 'dream', 'dress', 'drift', 'drill', 'drink', 'drip', 'drive', 'drop', 'drum', 'dry', 'duck', 'dumb', 'dune', 'during', 'dust', 'dutch', 'duty', 'dwarf', 'dynamic', 'eager', 'eagle', 'early', 'earn', 'earth', 'easily', 'east', 'easy', 'echo', 'ecology', 'economy', 'edge', 'edit', 'educate', 'effort', 'egg', 'eight', 'either', 'elbow', 'elder', 'electric', 'elegant', 'element', 'elephant', 'elevator', 'elite', 'else', 'embark', 'embody', 'embrace', 'emerge', 'emotion', 'employ', 'empower', 'empty', 'enable', 'enact', 'end', 'endless', 'endorse', 'enemy', 'energy', 'enforce', 'engage', 'engine', 'enhance', 'enjoy', 'enlist', 'enough', 'enrich', 'enroll', 'ensure', 'enter', 'entire', 'entry', 'envelope', 'episode', 'equal', 'equip', 'era', 'erase', 'erode', 'erosion', 'error', 'erupt', 'escape', 'essay', 'essence', 'estate', 'eternal', 'ethics', 'evidence', 'evil', 'evoke', 'evolve', 'exact', 'example', 'excess', 'exchange', 'excite', 'exclude', 'excuse', 'execute', 'exercise', 'exhaust', 'exhibit', 'exile', 'exist', 'exit', 'exotic', 'expand', 'expect', 'expire', 'explain', 'expose', 'express', 'extend', 'extra', 'eye', 'eyebrow', 'fabric', 'face', 'faculty', 'fade', 'faint', 'faith', 'fall', 'false', 'fame', 'family', 'famous', 'fan', 'fancy', 'fantasy', 'farm', 'fashion', 'fat', 'fatal', 'father', 'fatigue', 'fault', 'favorite', 'feature', 'february', 'federal', 'fee', 'feed', 'feel', 'female', 'fence', 'festival', 'fetch', 'fever', 'few', 'fiber', 'fiction', 'field', 'figure', 'file', 'film', 'filter', 'final', 'find', 'fine', 'finger', 'finish', 'fire', 'firm', 'first', 'fiscal', 'fish', 'fit', 'fitness', 'fix', 'flag', 'flame', 'flash', 'flat', 'flavor', 'flee', 'flight', 'flip', 'float', 'flock', 'floor', 'flower', 'fluid', 'flush', 'fly', 'foam', 'focus', 'fog', 'foil', 'fold', 'follow', 'food', 'foot', 'force', 'forest', 'forget', 'fork', 'fortune', 'forum', 'forward', 'fossil', 'foster', 'found', 'fox', 'fragile', 'frame', 'frequent', 'fresh', 'friend', 'fringe', 'frog', 'front', 'frost', 'frown', 'frozen', 'fruit', 'fuel', 'fun', 'funny', 'furnace', 'fury', 'future', 'gadget', 'gain', 'galaxy', 'gallery', 'game', 'gap', 'garage', 'garbage', 'garden', 'garlic', 'garment', 'gas', 'gasp', 'gate', 'gather', 'gauge', 'gaze', 'general', 'genius', 'genre', 'gentle', 'genuine', 'gesture', 'ghost', 'giant', 'gift', 'giggle', 'ginger', 'giraffe', 'girl', 'give', 'glad', 'glance', 'glare', 'glass', 'glide', 'glimpse', 'globe', 'gloom', 'glory', 'glove', 'glow', 'glue', 'goat', 'goddess', 'gold', 'good', 'goose', 'gorilla', 'gospel', 'gossip', 'govern', 'gown', 'grab', 'grace', 'grain', 'grant', 'grape', 'grass', 'gravity', 'great', 'green', 'grid', 'grief', 'grit', 'grocery', 'group', 'grow', 'grunt', 'guard', 'guess', 'guide', 'guilt', 'guitar', 'gun', 'gym', 'habit', 'hair', 'half', 'hammer', 'hamster', 'hand', 'happy', 'harbor', 'hard', 'harsh', 'harvest', 'hat', 'have', 'hawk', 'hazard', 'head', 'health', 'heart', 'heavy', 'hedgehog', 'height', 'hello', 'helmet', 'help', 'hen', 'hero', 'hidden', 'high', 'hill', 'hint', 'hip', 'hire', 'history', 'hobby', 'hockey', 'hold', 'hole', 'holiday', 'hollow', 'home', 'honey', 'hood', 'hope', 'horn', 'horror', 'horse', 'hospital', 'host', 'hotel', 'hour', 'hover', 'hub', 'huge', 'human', 'humble', 'humor', 'hundred', 'hungry', 'hunt', 'hurdle', 'hurry', 'hurt', 'husband', 'hybrid', 'ice', 'icon', 'idea', 'identify', 'idle', 'ignore', 'ill', 'illegal', 'illness', 'image', 'imitate', 'immense', 'immune', 'impact', 'impose', 'improve', 'impulse', 'inch', 'include', 'income', 'increase', 'index', 'indicate', 'indoor', 'industry', 'infant', 'inflict', 'inform', 'inhale', 'inherit', 'initial', 'inject', 'injury', 'inmate', 'inner', 'innocent', 'input', 'inquiry', 'insane', 'insect', 'inside', 'inspire', 'install', 'intact', 'interest', 'into', 'invest', 'invite', 'involve', 'iron', 'island', 'isolate', 'issue', 'item', 'ivory', 'jacket', 'jaguar', 'jar', 'jazz', 'jealous', 'jeans', 'jelly', 'jewel', 'job', 'join', 'joke', 'journey', 'joy', 'judge', 'juice', 'jump', 'jungle', 'junior', 'junk', 'just', 'kangaroo', 'keen', 'keep', 'ketchup', 'key', 'kick', 'kid', 'kidney', 'kind', 'kingdom', 'kiss', 'kit', 'kitchen', 'kite', 'kitten', 'kiwi', 'knee', 'knife', 'knock', 'know', 'lab', 'label', 'labor', 'ladder', 'lady', 'lake', 'lamp', 'language', 'laptop', 'large', 'later', 'latin', 'laugh', 'laundry', 'lava', 'law', 'lawn', 'lawsuit', 'layer', 'lazy', 'leader', 'leaf', 'learn', 'leave', 'lecture', 'left', 'leg', 'legal', 'legend', 'leisure', 'lemon', 'lend', 'length', 'lens', 'leopard', 'lesson', 'letter', 'level', 'liar', 'liberty', 'library', 'license', 'life', 'lift', 'light', 'like', 'limb', 'limit', 'link', 'lion', 'liquid', 'list', 'little', 'live', 'lizard', 'load', 'loan', 'lobster', 'local', 'lock', 'logic', 'lonely', 'long', 'loop', 'lottery', 'loud', 'lounge', 'love', 'loyal', 'lucky', 'luggage', 'lumber', 'lunar', 'lunch', 'luxury', 'lyrics', 'machine', 'mad', 'magic', 'magnet', 'maid', 'mail', 'main', 'major', 'make', 'mammal', 'man', 'manage', 'mandate', 'mango', 'mansion', 'manual', 'maple', 'marble', 'march', 'margin', 'marine', 'market', 'marriage', 'mask', 'mass', 'master', 'match', 'material', 'math', 'matrix', 'matter', 'maximum', 'maze', 'meadow', 'mean', 'measure', 'meat', 'mechanic', 'medal', 'media', 'melody', 'melt', 'member', 'memory', 'mention', 'menu', 'mercy', 'merge', 'merit', 'merry', 'mesh', 'message', 'metal', 'method', 'middle', 'midnight', 'milk', 'million', 'mimic', 'mind', 'minimum', 'minor', 'minute', 'miracle', 'mirror', 'misery', 'miss', 'mistake', 'mix', 'mixed', 'mixture', 'mobile', 'model', 'modify', 'mom', 'moment', 'monitor', 'monkey', 'monster', 'month', 'moon', 'moral', 'more', 'morning', 'mosquito', 'mother', 'motion', 'motor', 'mountain', 'mouse', 'move', 'movie', 'much', 'muffin', 'mule', 'multiply', 'muscle', 'museum', 'mushroom', 'music', 'must', 'mutual', 'myself', 'mystery', 'myth', 'naive', 'name', 'napkin', 'narrow', 'nasty', 'nation', 'nature', 'near', 'neck', 'need', 'negative', 'neglect', 'neither', 'nephew', 'nerve', 'nest', 'net', 'network', 'neutral', 'never', 'news', 'next', 'nice', 'night', 'noble', 'noise', 'nominee', 'noodle', 'normal', 'north', 'nose', 'notable', 'note', 'nothing', 'notice', 'novel', 'now', 'nuclear', 'number', 'nurse', 'nut', 'oak', 'obey', 'object', 'oblige', 'obscure', 'observe', 'obtain', 'obvious', 'occur', 'ocean', 'october', 'odor', 'off', 'offer', 'office', 'often', 'oil', 'okay', 'old', 'olive', 'olympic', 'omit', 'once', 'one', 'onion', 'online', 'only', 'open', 'opera', 'opinion', 'oppose', 'option', 'orange', 'orbit', 'orchard', 'order', 'ordinary', 'organ', 'orient', 'original', 'orphan', 'ostrich', 'other', 'outdoor', 'outer', 'output', 'outside', 'oval', 'oven', 'over', 'own', 'owner', 'oxygen', 'oyster', 'ozone', 'pact', 'paddle', 'page', 'pair', 'palace', 'palm', 'panda', 'panel', 'panic', 'panther', 'paper', 'parade', 'parent', 'park', 'parrot', 'party', 'pass', 'patch', 'path', 'patient', 'patrol', 'pattern', 'pause', 'pave', 'payment', 'peace', 'peanut', 'pear', 'peasant', 'pelican', 'pen', 'penalty', 'pencil', 'people', 'pepper', 'perfect', 'permit', 'person', 'pet', 'phone', 'photo', 'phrase', 'physical', 'piano', 'picnic', 'picture', 'piece', 'pig', 'pigeon', 'pill', 'pilot', 'pink', 'pioneer', 'pipe', 'pistol', 'pitch', 'pizza', 'place', 'planet', 'plastic', 'plate', 'play', 'please', 'pledge', 'pluck', 'plug', 'plunge', 'poem', 'poet', 'point', 'polar', 'pole', 'police', 'pond', 'pony', 'pool', 'popular', 'portion', 'position', 'possible', 'post', 'potato', 'pottery', 'poverty', 'powder', 'power', 'practice', 'praise', 'predict', 'prefer', 'prepare', 'present', 'pretty', 'prevent', 'price', 'pride', 'primary', 'print', 'priority', 'prison', 'private', 'prize', 'problem', 'process', 'produce', 'profit', 'program', 'project', 'promote', 'proof', 'property', 'prosper', 'protect', 'proud', 'provide', 'public', 'pudding', 'pull', 'pulp', 'pulse', 'pumpkin', 'punch', 'pupil', 'puppy', 'purchase', 'purity', 'purpose', 'purse', 'push', 'put', 'puzzle', 'pyramid', 'quality', 'quantum', 'quarter', 'question', 'quick', 'quit', 'quiz', 'quote', 'rabbit', 'raccoon', 'race', 'rack', 'radar', 'radio', 'rail', 'rain', 'raise', 'rally', 'ramp', 'ranch', 'random', 'range', 'rapid', 'rare', 'rate', 'rather', 'raven', 'raw', 'razor', 'ready', 'real', 'reason', 'rebel', 'rebuild', 'recall', 'receive', 'recipe', 'record', 'recycle', 'reduce', 'reflect', 'reform', 'refuse', 'region', 'regret', 'regular', 'reject', 'relax', 'release', 'relief', 'rely', 'remain', 'remember', 'remind', 'remove', 'render', 'renew', 'rent', 'reopen', 'repair', 'repeat', 'replace', 'report', 'require', 'rescue', 'resemble', 'resist', 'resource', 'response', 'result', 'retire', 'retreat', 'return', 'reunion', 'reveal', 'review', 'reward', 'rhythm', 'rib', 'ribbon', 'rice', 'rich', 'ride', 'ridge', 'rifle', 'right', 'rigid', 'ring', 'riot', 'ripple', 'risk', 'ritual', 'rival', 'river', 'road', 'roast', 'robot', 'robust', 'rocket', 'romance', 'roof', 'rookie', 'room', 'rose', 'rotate', 'rough', 'round', 'route', 'royal', 'rubber', 'rude', 'rug', 'rule', 'run', 'runway', 'rural', 'sad', 'saddle', 'sadness', 'safe', 'sail', 'salad', 'salmon', 'salon', 'salt', 'salute', 'same', 'sample', 'sand', 'satisfy', 'satoshi', 'sauce', 'sausage', 'save', 'say', 'scale', 'scan', 'scare', 'scatter', 'scene', 'scheme', 'school', 'science', 'scissors', 'scorpion', 'scout', 'scrap', 'screen', 'script', 'scrub', 'sea', 'search', 'season', 'seat', 'second', 'secret', 'section', 'security', 'seed', 'seek', 'segment', 'select', 'sell', 'seminar', 'senior', 'sense', 'sentence', 'series', 'service', 'session', 'settle', 'setup', 'seven', 'shadow', 'shaft', 'shallow', 'share', 'shed', 'shell', 'sheriff', 'shield', 'shift', 'shine', 'ship', 'shiver', 'shock', 'shoe', 'shoot', 'shop', 'short', 'shoulder', 'shove', 'shrimp', 'shrug', 'shuffle', 'shy', 'sibling', 'sick', 'side', 'siege', 'sight', 'sign', 'silent', 'silk', 'silly', 'silver', 'similar', 'simple', 'since', 'sing', 'siren', 'sister', 'situate', 'six', 'size', 'skate', 'sketch', 'ski', 'skill', 'skin', 'skirt', 'skull', 'slab', 'slam', 'sleep', 'slender', 'slice', 'slide', 'slight', 'slim', 'slogan', 'slot', 'slow', 'slush', 'small', 'smart', 'smile', 'smoke', 'smooth', 'snack', 'snake', 'snap', 'sniff', 'snow', 'soap', 'soccer', 'social', 'sock', 'soda', 'soft', 'solar', 'soldier', 'solid', 'solution', 'solve', 'someone', 'song', 'soon', 'sorry', 'sort', 'soul', 'sound', 'soup', 'source', 'south', 'space', 'spare', 'spatial', 'spawn', 'speak', 'special', 'speed', 'spell', 'spend', 'sphere', 'spice', 'spider', 'spike', 'spin', 'spirit', 'split', 'spoil', 'sponsor', 'spoon', 'sport', 'spot', 'spray', 'spread', 'spring', 'spy', 'square', 'squeeze', 'squirrel', 'stable', 'stadium', 'staff', 'stage', 'stairs', 'stamp', 'stand', 'start', 'state', 'stay', 'steak', 'steel', 'stem', 'step', 'stereo', 'stick', 'still', 'sting', 'stock', 'stomach', 'stone', 'stool', 'story', 'stove', 'strategy', 'street', 'strike', 'strong', 'struggle', 'student', 'stuff', 'stumble', 'style', 'subject', 'submit', 'subway', 'success', 'such', 'sudden', 'suffer', 'sugar', 'suggest', 'suit', 'summer', 'sun', 'sunny', 'sunset', 'super', 'supply', 'supreme', 'sure', 'surface', 'surge', 'surprise', 'surround', 'survey', 'suspect', 'sustain', 'swallow', 'swamp', 'swap', 'swarm', 'swear', 'sweet', 'swift', 'swim', 'swing', 'switch', 'sword', 'symbol', 'symptom', 'syrup', 'system', 'table', 'tackle', 'tag', 'tail', 'talent', 'talk', 'tank', 'tape', 'target', 'task', 'taste', 'tattoo', 'taxi', 'teach', 'team', 'tell', 'ten', 'tenant', 'tennis', 'tent', 'term', 'test', 'text', 'thank', 'that', 'theme', 'then', 'theory', 'there', 'they', 'thing', 'this', 'thought', 'three', 'thrive', 'throw', 'thumb', 'thunder', 'ticket', 'tide', 'tiger', 'tilt', 'timber', 'time', 'tiny', 'tip', 'tired', 'tissue', 'title', 'toast', 'tobacco', 'today', 'toddler', 'toe', 'together', 'toilet', 'token', 'tomato', 'tomorrow', 'tone', 'tongue', 'tonight', 'tool', 'tooth', 'top', 'topic', 'topple', 'torch', 'tornado', 'tortoise', 'toss', 'total', 'tourist', 'toward', 'tower', 'town', 'toy', 'track', 'trade', 'traffic', 'tragic', 'train', 'transfer', 'trap', 'trash', 'travel', 'tray', 'treat', 'tree', 'trend', 'trial', 'tribe', 'trick', 'trigger', 'trim', 'trip', 'trophy', 'trouble', 'truck', 'true', 'truly', 'trumpet', 'trust', 'truth', 'try', 'tube', 'tuition', 'tumble', 'tuna', 'tunnel', 'turkey', 'turn', 'turtle', 'twelve', 'twenty', 'twice', 'twin', 'twist', 'two', 'type', 'typical', 'ugly', 'umbrella', 'unable', 'unaware', 'uncle', 'uncover', 'under', 'undo', 'unfair', 'unfold', 'unhappy', 'uniform', 'unique', 'unit', 'universe', 'unknown', 'unlock', 'until', 'unusual', 'unveil', 'update', 'upgrade', 'uphold', 'upon', 'upper', 'upset', 'urban', 'urge', 'usage', 'use', 'used', 'useful', 'useless', 'usual', 'utility', 'vacant', 'vacuum', 'vague', 'valid', 'valley', 'valve', 'van', 'vanish', 'vapor', 'various', 'vast', 'vault', 'vehicle', 'velvet', 'vendor', 'venture', 'venue', 'verb', 'verify', 'version', 'very', 'vessel', 'veteran', 'viable', 'vibrant', 'vicious', 'victory', 'video', 'view', 'village', 'vintage', 'violin', 'virtual', 'virus', 'visa', 'visit', 'visual', 'vital', 'vivid', 'vocal', 'voice', 'void', 'volcano', 'volume', 'vote', 'voyage', 'wage', 'wagon', 'wait', 'walk', 'wall', 'walnut', 'want', 'warfare', 'warm', 'warrior', 'wash', 'wasp', 'waste', 'water', 'wave', 'way', 'wealth', 'weapon', 'wear', 'weasel', 'weather', 'web', 'wedding', 'weekend', 'weird', 'welcome', 'west', 'wet', 'whale', 'what', 'wheat', 'wheel', 'when', 'where', 'whip', 'whisper', 'wide', 'width', 'wife', 'wild', 'will', 'win', 'window', 'wine', 'wing', 'wink', 'winner', 'winter', 'wire', 'wisdom', 'wise', 'wish', 'witness', 'wolf', 'woman', 'wonder', 'wood', 'wool', 'word', 'work', 'world', 'worry', 'worth', 'wrap', 'wreck', 'wrestle', 'wrist', 'write', 'wrong', 'yard', 'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone', 'zoo']) +def generate_mnemonic(strength=256, wordlist=['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual', 'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance', 'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent', 'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album', 'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone', 'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among', 'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry', 'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique', 'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april', 'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor', 'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact', 'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume', 'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction', 'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado', 'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis', 'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball', 'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base', 'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become', 'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt', 'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle', 'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black', 'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood', 'blossom', 'blouse', 'blue', 'blur', 'blush', 'board', 'boat', 'body', 'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring', 'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain', 'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief', 'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother', 'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb', 'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus', 'business', 'busy', 'butter', 'buyer', 'buzz', 'cabbage', 'cabin', 'cable', 'cactus', 'cage', 'cake', 'call', 'calm', 'camera', 'camp', 'can', 'canal', 'cancel', 'candy', 'cannon', 'canoe', 'canvas', 'canyon', 'capable', 'capital', 'captain', 'car', 'carbon', 'card', 'cargo', 'carpet', 'carry', 'cart', 'case', 'cash', 'casino', 'castle', 'casual', 'cat', 'catalog', 'catch', 'category', 'cattle', 'caught', 'cause', 'caution', 'cave', 'ceiling', 'celery', 'cement', 'census', 'century', 'cereal', 'certain', 'chair', 'chalk', 'champion', 'change', 'chaos', 'chapter', 'charge', 'chase', 'chat', 'cheap', 'check', 'cheese', 'chef', 'cherry', 'chest', 'chicken', 'chief', 'child', 'chimney', 'choice', 'choose', 'chronic', 'chuckle', 'chunk', 'churn', 'cigar', 'cinnamon', 'circle', 'citizen', 'city', 'civil', 'claim', 'clap', 'clarify', 'claw', 'clay', 'clean', 'clerk', 'clever', 'click', 'client', 'cliff', 'climb', 'clinic', 'clip', 'clock', 'clog', 'close', 'cloth', 'cloud', 'clown', 'club', 'clump', 'cluster', 'clutch', 'coach', 'coast', 'coconut', 'code', 'coffee', 'coil', 'coin', 'collect', 'color', 'column', 'combine', 'come', 'comfort', 'comic', 'common', 'company', 'concert', 'conduct', 'confirm', 'congress', 'connect', 'consider', 'control', 'convince', 'cook', 'cool', 'copper', 'copy', 'coral', 'core', 'corn', 'correct', 'cost', 'cotton', 'couch', 'country', 'couple', 'course', 'cousin', 'cover', 'coyote', 'crack', 'cradle', 'craft', 'cram', 'crane', 'crash', 'crater', 'crawl', 'crazy', 'cream', 'credit', 'creek', 'crew', 'cricket', 'crime', 'crisp', 'critic', 'crop', 'cross', 'crouch', 'crowd', 'crucial', 'cruel', 'cruise', 'crumble', 'crunch', 'crush', 'cry', 'crystal', 'cube', 'culture', 'cup', 'cupboard', 'curious', 'current', 'curtain', 'curve', 'cushion', 'custom', 'cute', 'cycle', 'dad', 'damage', 'damp', 'dance', 'danger', 'daring', 'dash', 'daughter', 'dawn', 'day', 'deal', 'debate', 'debris', 'decade', 'december', 'decide', 'decline', 'decorate', 'decrease', 'deer', 'defense', 'define', 'defy', 'degree', 'delay', 'deliver', 'demand', 'demise', 'denial', 'dentist', 'deny', 'depart', 'depend', 'deposit', 'depth', 'deputy', 'derive', 'describe', 'desert', 'design', 'desk', 'despair', 'destroy', 'detail', 'detect', 'develop', 'device', 'devote', 'diagram', 'dial', 'diamond', 'diary', 'dice', 'diesel', 'diet', 'differ', 'digital', 'dignity', 'dilemma', 'dinner', 'dinosaur', 'direct', 'dirt', 'disagree', 'discover', 'disease', 'dish', 'dismiss', 'disorder', 'display', 'distance', 'divert', 'divide', 'divorce', 'dizzy', 'doctor', 'document', 'dog', 'doll', 'dolphin', 'domain', 'donate', 'donkey', 'donor', 'door', 'dose', 'double', 'dove', 'draft', 'dragon', 'drama', 'drastic', 'draw', 'dream', 'dress', 'drift', 'drill', 'drink', 'drip', 'drive', 'drop', 'drum', 'dry', 'duck', 'dumb', 'dune', 'during', 'dust', 'dutch', 'duty', 'dwarf', 'dynamic', 'eager', 'eagle', 'early', 'earn', 'earth', 'easily', 'east', 'easy', 'echo', 'ecology', 'economy', 'edge', 'edit', 'educate', 'effort', 'egg', 'eight', 'either', 'elbow', 'elder', 'electric', 'elegant', 'element', 'elephant', 'elevator', 'elite', 'else', 'embark', 'embody', 'embrace', 'emerge', 'emotion', 'employ', 'empower', 'empty', 'enable', 'enact', 'end', 'endless', 'endorse', 'enemy', 'energy', 'enforce', 'engage', 'engine', 'enhance', 'enjoy', 'enlist', 'enough', 'enrich', 'enroll', 'ensure', 'enter', 'entire', 'entry', 'envelope', 'episode', 'equal', 'equip', 'era', 'erase', 'erode', 'erosion', 'error', 'erupt', 'escape', 'essay', 'essence', 'estate', 'eternal', 'ethics', 'evidence', 'evil', 'evoke', 'evolve', 'exact', 'example', 'excess', 'exchange', 'excite', 'exclude', 'excuse', 'execute', 'exercise', 'exhaust', 'exhibit', 'exile', 'exist', 'exit', 'exotic', 'expand', 'expect', 'expire', 'explain', 'expose', 'express', 'extend', 'extra', 'eye', 'eyebrow', 'fabric', 'face', 'faculty', 'fade', 'faint', 'faith', 'fall', 'false', 'fame', 'family', 'famous', 'fan', 'fancy', 'fantasy', 'farm', 'fashion', 'fat', 'fatal', 'father', 'fatigue', 'fault', 'favorite', 'feature', 'february', 'federal', 'fee', 'feed', 'feel', 'female', 'fence', 'festival', 'fetch', 'fever', 'few', 'fiber', 'fiction', 'field', 'figure', 'file', 'film', 'filter', 'final', 'find', 'fine', 'finger', 'finish', 'fire', 'firm', 'first', 'fiscal', 'fish', 'fit', 'fitness', 'fix', 'flag', 'flame', 'flash', 'flat', 'flavor', 'flee', 'flight', 'flip', 'float', 'flock', 'floor', 'flower', 'fluid', 'flush', 'fly', 'foam', 'focus', 'fog', 'foil', 'fold', 'follow', 'food', 'foot', 'force', 'forest', 'forget', 'fork', 'fortune', 'forum', 'forward', 'fossil', 'foster', 'found', 'fox', 'fragile', 'frame', 'frequent', 'fresh', 'friend', 'fringe', 'frog', 'front', 'frost', 'frown', 'frozen', 'fruit', 'fuel', 'fun', 'funny', 'furnace', 'fury', 'future', 'gadget', 'gain', 'galaxy', 'gallery', 'game', 'gap', 'garage', 'garbage', 'garden', 'garlic', 'garment', 'gas', 'gasp', 'gate', 'gather', 'gauge', 'gaze', 'general', 'genius', 'genre', 'gentle', 'genuine', 'gesture', 'ghost', 'giant', 'gift', 'giggle', 'ginger', 'giraffe', 'girl', 'give', 'glad', 'glance', 'glare', 'glass', 'glide', 'glimpse', 'globe', 'gloom', 'glory', 'glove', 'glow', 'glue', 'goat', 'goddess', 'gold', 'good', 'goose', 'gorilla', 'gospel', 'gossip', 'govern', 'gown', 'grab', 'grace', 'grain', 'grant', 'grape', 'grass', 'gravity', 'great', 'green', 'grid', 'grief', 'grit', 'grocery', 'group', 'grow', 'grunt', 'guard', 'guess', 'guide', 'guilt', 'guitar', 'gun', 'gym', 'habit', 'hair', 'half', 'hammer', 'hamster', 'hand', 'happy', 'harbor', 'hard', 'harsh', 'harvest', 'hat', 'have', 'hawk', 'hazard', 'head', 'health', 'heart', 'heavy', 'hedgehog', 'height', 'hello', 'helmet', 'help', 'hen', 'hero', 'hidden', 'high', 'hill', 'hint', 'hip', 'hire', 'history', 'hobby', 'hockey', 'hold', 'hole', 'holiday', 'hollow', 'home', 'honey', 'hood', 'hope', 'horn', 'horror', 'horse', 'hospital', 'host', 'hotel', 'hour', 'hover', 'hub', 'huge', 'human', 'humble', 'humor', 'hundred', 'hungry', 'hunt', 'hurdle', 'hurry', 'hurt', 'husband', 'hybrid', 'ice', 'icon', 'idea', 'identify', 'idle', 'ignore', 'ill', 'illegal', 'illness', 'image', 'imitate', 'immense', 'immune', 'impact', 'impose', 'improve', 'impulse', 'inch', 'include', 'income', 'increase', 'index', 'indicate', 'indoor', 'industry', 'infant', 'inflict', 'inform', 'inhale', 'inherit', 'initial', 'inject', 'injury', 'inmate', 'inner', 'innocent', 'input', 'inquiry', 'insane', 'insect', 'inside', 'inspire', 'install', 'intact', 'interest', 'into', 'invest', 'invite', 'involve', 'iron', 'island', 'isolate', 'issue', 'item', 'ivory', 'jacket', 'jaguar', 'jar', 'jazz', 'jealous', 'jeans', 'jelly', 'jewel', 'job', 'join', 'joke', 'journey', 'joy', 'judge', 'juice', 'jump', 'jungle', 'junior', 'junk', 'just', 'kangaroo', 'keen', 'keep', 'ketchup', 'key', 'kick', 'kid', 'kidney', 'kind', 'kingdom', 'kiss', 'kit', 'kitchen', 'kite', 'kitten', 'kiwi', 'knee', 'knife', 'knock', 'know', 'lab', 'label', 'labor', 'ladder', 'lady', 'lake', 'lamp', 'language', 'laptop', 'large', 'later', 'latin', 'laugh', 'laundry', 'lava', 'law', 'lawn', 'lawsuit', 'layer', 'lazy', 'leader', 'leaf', 'learn', 'leave', 'lecture', 'left', 'leg', 'legal', 'legend', 'leisure', 'lemon', 'lend', 'length', 'lens', 'leopard', 'lesson', 'letter', 'level', 'liar', 'liberty', 'library', 'license', 'life', 'lift', 'light', 'like', 'limb', 'limit', 'link', 'lion', 'liquid', 'list', 'little', 'live', 'lizard', 'load', 'loan', 'lobster', 'local', 'lock', 'logic', 'lonely', 'long', 'loop', 'lottery', 'loud', 'lounge', 'love', 'loyal', 'lucky', 'luggage', 'lumber', 'lunar', 'lunch', 'luxury', 'lyrics', 'machine', 'mad', 'magic', 'magnet', 'maid', 'mail', 'main', 'major', 'make', 'mammal', 'man', 'manage', 'mandate', 'mango', 'mansion', 'manual', 'maple', 'marble', 'march', 'margin', 'marine', 'market', 'marriage', 'mask', 'mass', 'master', 'match', 'material', 'math', 'matrix', 'matter', 'maximum', 'maze', 'meadow', 'mean', 'measure', 'meat', 'mechanic', 'medal', 'media', 'melody', 'melt', 'member', 'memory', 'mention', 'menu', 'mercy', 'merge', 'merit', 'merry', 'mesh', 'message', 'metal', 'method', 'middle', 'midnight', 'milk', 'million', 'mimic', 'mind', 'minimum', 'minor', 'minute', 'miracle', 'mirror', 'misery', 'miss', 'mistake', 'mix', 'mixed', 'mixture', 'mobile', 'model', 'modify', 'mom', 'moment', 'monitor', 'monkey', 'monster', 'month', 'moon', 'moral', 'more', 'morning', 'mosquito', 'mother', 'motion', 'motor', 'mountain', 'mouse', 'move', 'movie', 'much', 'muffin', 'mule', 'multiply', 'muscle', 'museum', 'mushroom', 'music', 'must', 'mutual', 'myself', 'mystery', 'myth', 'naive', 'name', 'napkin', 'narrow', 'nasty', 'nation', 'nature', 'near', 'neck', 'need', 'negative', 'neglect', 'neither', 'nephew', 'nerve', 'nest', 'net', 'network', 'neutral', 'never', 'news', 'next', 'nice', 'night', 'noble', 'noise', 'nominee', 'noodle', 'normal', 'north', 'nose', 'notable', 'note', 'nothing', 'notice', 'novel', 'now', 'nuclear', 'number', 'nurse', 'nut', 'oak', 'obey', 'object', 'oblige', 'obscure', 'observe', 'obtain', 'obvious', 'occur', 'ocean', 'october', 'odor', 'off', 'offer', 'office', 'often', 'oil', 'okay', 'old', 'olive', 'olympic', 'omit', 'once', 'one', 'onion', 'online', 'only', 'open', 'opera', 'opinion', 'oppose', 'option', 'orange', 'orbit', 'orchard', 'order', 'ordinary', 'organ', 'orient', 'original', 'orphan', 'ostrich', 'other', 'outdoor', 'outer', 'output', 'outside', 'oval', 'oven', 'over', 'own', 'owner', 'oxygen', 'oyster', 'ozone', 'pact', 'paddle', 'page', 'pair', 'palace', 'palm', 'panda', 'panel', 'panic', 'panther', 'paper', 'parade', 'parent', 'park', 'parrot', 'party', 'pass', 'patch', 'path', 'patient', 'patrol', 'pattern', 'pause', 'pave', 'payment', 'peace', 'peanut', 'pear', 'peasant', 'pelican', 'pen', 'penalty', 'pencil', 'people', 'pepper', 'perfect', 'permit', 'person', 'pet', 'phone', 'photo', 'phrase', 'physical', 'piano', 'picnic', 'picture', 'piece', 'pig', 'pigeon', 'pill', 'pilot', 'pink', 'pioneer', 'pipe', 'pistol', 'pitch', 'pizza', 'place', 'planet', 'plastic', 'plate', 'play', 'please', 'pledge', 'pluck', 'plug', 'plunge', 'poem', 'poet', 'point', 'polar', 'pole', 'police', 'pond', 'pony', 'pool', 'popular', 'portion', 'position', 'possible', 'post', 'potato', 'pottery', 'poverty', 'powder', 'power', 'practice', 'praise', 'predict', 'prefer', 'prepare', 'present', 'pretty', 'prevent', 'price', 'pride', 'primary', 'print', 'priority', 'prison', 'private', 'prize', 'problem', 'process', 'produce', 'profit', 'program', 'project', 'promote', 'proof', 'property', 'prosper', 'protect', 'proud', 'provide', 'public', 'pudding', 'pull', 'pulp', 'pulse', 'pumpkin', 'punch', 'pupil', 'puppy', 'purchase', 'purity', 'purpose', 'purse', 'push', 'put', 'puzzle', 'pyramid', 'quality', 'quantum', 'quarter', 'question', 'quick', 'quit', 'quiz', 'quote', 'rabbit', 'raccoon', 'race', 'rack', 'radar', 'radio', 'rail', 'rain', 'raise', 'rally', 'ramp', 'ranch', 'random', 'range', 'rapid', 'rare', 'rate', 'rather', 'raven', 'raw', 'razor', 'ready', 'real', 'reason', 'rebel', 'rebuild', 'recall', 'receive', 'recipe', 'record', 'recycle', 'reduce', 'reflect', 'reform', 'refuse', 'region', 'regret', 'regular', 'reject', 'relax', 'release', 'relief', 'rely', 'remain', 'remember', 'remind', 'remove', 'render', 'renew', 'rent', 'reopen', 'repair', 'repeat', 'replace', 'report', 'require', 'rescue', 'resemble', 'resist', 'resource', 'response', 'result', 'retire', 'retreat', 'return', 'reunion', 'reveal', 'review', 'reward', 'rhythm', 'rib', 'ribbon', 'rice', 'rich', 'ride', 'ridge', 'rifle', 'right', 'rigid', 'ring', 'riot', 'ripple', 'risk', 'ritual', 'rival', 'river', 'road', 'roast', 'robot', 'robust', 'rocket', 'romance', 'roof', 'rookie', 'room', 'rose', 'rotate', 'rough', 'round', 'route', 'royal', 'rubber', 'rude', 'rug', 'rule', 'run', 'runway', 'rural', 'sad', 'saddle', 'sadness', 'safe', 'sail', 'salad', 'salmon', 'salon', 'salt', 'salute', 'same', 'sample', 'sand', 'satisfy', 'satoshi', 'sauce', 'sausage', 'save', 'say', 'scale', 'scan', 'scare', 'scatter', 'scene', 'scheme', 'school', 'science', 'scissors', 'scorpion', 'scout', 'scrap', 'screen', 'script', 'scrub', 'sea', 'search', 'season', 'seat', 'second', 'secret', 'section', 'security', 'seed', 'seek', 'segment', 'select', 'sell', 'seminar', 'senior', 'sense', 'sentence', 'series', 'service', 'session', 'settle', 'setup', 'seven', 'shadow', 'shaft', 'shallow', 'share', 'shed', 'shell', 'sheriff', 'shield', 'shift', 'shine', 'ship', 'shiver', 'shock', 'shoe', 'shoot', 'shop', 'short', 'shoulder', 'shove', 'shrimp', 'shrug', 'shuffle', 'shy', 'sibling', 'sick', 'side', 'siege', 'sight', 'sign', 'silent', 'silk', 'silly', 'silver', 'similar', 'simple', 'since', 'sing', 'siren', 'sister', 'situate', 'six', 'size', 'skate', 'sketch', 'ski', 'skill', 'skin', 'skirt', 'skull', 'slab', 'slam', 'sleep', 'slender', 'slice', 'slide', 'slight', 'slim', 'slogan', 'slot', 'slow', 'slush', 'small', 'smart', 'smile', 'smoke', 'smooth', 'snack', 'snake', 'snap', 'sniff', 'snow', 'soap', 'soccer', 'social', 'sock', 'soda', 'soft', 'solar', 'soldier', 'solid', 'solution', 'solve', 'someone', 'song', 'soon', 'sorry', 'sort', 'soul', 'sound', 'soup', 'source', 'south', 'space', 'spare', 'spatial', 'spawn', 'speak', 'special', 'speed', 'spell', 'spend', 'sphere', 'spice', 'spider', 'spike', 'spin', 'spirit', 'split', 'spoil', 'sponsor', 'spoon', 'sport', 'spot', 'spray', 'spread', 'spring', 'spy', 'square', 'squeeze', 'squirrel', 'stable', 'stadium', 'staff', 'stage', 'stairs', 'stamp', 'stand', 'start', 'state', 'stay', 'steak', 'steel', 'stem', 'step', 'stereo', 'stick', 'still', 'sting', 'stock', 'stomach', 'stone', 'stool', 'story', 'stove', 'strategy', 'street', 'strike', 'strong', 'struggle', 'student', 'stuff', 'stumble', 'style', 'subject', 'submit', 'subway', 'success', 'such', 'sudden', 'suffer', 'sugar', 'suggest', 'suit', 'summer', 'sun', 'sunny', 'sunset', 'super', 'supply', 'supreme', 'sure', 'surface', 'surge', 'surprise', 'surround', 'survey', 'suspect', 'sustain', 'swallow', 'swamp', 'swap', 'swarm', 'swear', 'sweet', 'swift', 'swim', 'swing', 'switch', 'sword', 'symbol', 'symptom', 'syrup', 'system', 'table', 'tackle', 'tag', 'tail', 'talent', 'talk', 'tank', 'tape', 'target', 'task', 'taste', 'tattoo', 'taxi', 'teach', 'team', 'tell', 'ten', 'tenant', 'tennis', 'tent', 'term', 'test', 'text', 'thank', 'that', 'theme', 'then', 'theory', 'there', 'they', 'thing', 'this', 'thought', 'three', 'thrive', 'throw', 'thumb', 'thunder', 'ticket', 'tide', 'tiger', 'tilt', 'timber', 'time', 'tiny', 'tip', 'tired', 'tissue', 'title', 'toast', 'tobacco', 'today', 'toddler', 'toe', 'together', 'toilet', 'token', 'tomato', 'tomorrow', 'tone', 'tongue', 'tonight', 'tool', 'tooth', 'top', 'topic', 'topple', 'torch', 'tornado', 'tortoise', 'toss', 'total', 'tourist', 'toward', 'tower', 'town', 'toy', 'track', 'trade', 'traffic', 'tragic', 'train', 'transfer', 'trap', 'trash', 'travel', 'tray', 'treat', 'tree', 'trend', 'trial', 'tribe', 'trick', 'trigger', 'trim', 'trip', 'trophy', 'trouble', 'truck', 'true', 'truly', 'trumpet', 'trust', 'truth', 'try', 'tube', 'tuition', 'tumble', 'tuna', 'tunnel', 'turkey', 'turn', 'turtle', 'twelve', 'twenty', 'twice', 'twin', 'twist', 'two', 'type', 'typical', 'ugly', 'umbrella', 'unable', 'unaware', 'uncle', 'uncover', 'under', 'undo', 'unfair', 'unfold', 'unhappy', 'uniform', 'unique', 'unit', 'universe', 'unknown', 'unlock', 'until', 'unusual', 'unveil', 'update', 'upgrade', 'uphold', 'upon', 'upper', 'upset', 'urban', 'urge', 'usage', 'use', 'used', 'useful', 'useless', 'usual', 'utility', 'vacant', 'vacuum', 'vague', 'valid', 'valley', 'valve', 'van', 'vanish', 'vapor', 'various', 'vast', 'vault', 'vehicle', 'velvet', 'vendor', 'venture', 'venue', 'verb', 'verify', 'version', 'very', 'vessel', 'veteran', 'viable', 'vibrant', 'vicious', 'victory', 'video', 'view', 'village', 'vintage', 'violin', 'virtual', 'virus', 'visa', 'visit', 'visual', 'vital', 'vivid', 'vocal', 'voice', 'void', 'volcano', 'volume', 'vote', 'voyage', 'wage', 'wagon', 'wait', 'walk', 'wall', 'walnut', 'want', 'warfare', 'warm', 'warrior', 'wash', 'wasp', 'waste', 'water', 'wave', 'way', 'wealth', 'weapon', 'wear', 'weasel', 'weather', 'web', 'wedding', 'weekend', 'weird', 'welcome', 'west', 'wet', 'whale', 'what', 'wheat', 'wheel', 'when', 'where', 'whip', 'whisper', 'wide', 'width', 'wife', 'wild', 'will', 'win', 'window', 'wine', 'wing', 'wink', 'winner', 'winter', 'wire', 'wisdom', 'wise', 'wish', 'witness', 'wolf', 'woman', 'wonder', 'wood', 'wool', 'word', 'work', 'world', 'worry', 'worth', 'wrap', 'wreck', 'wrestle', 'wrist', 'write', 'wrong', 'yard', 'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone', 'zoo'])
-

Generate random mnemonic

+

Generate random mnemonic

Args

strength : int, optional
The number of bits of the generated key. Defaults to 256.
-
wordlist : list[str], optional
+
wordlist : list[str], optional
The Word list used to generate the mnemonic. Defaults to wordlist.

Returns

-
str
+
str
A string of space separated words representing the mnemonic.
-
+
-Source code + +Expand source code +
def generate_mnemonic(strength=256, wordlist=wordlist):
     """Generate random mnemonic
 
@@ -232,24 +242,26 @@ 

Returns

-def key_to_mnemonic(key, wordlist=['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual', 'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance', 'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent', 'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album', 'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone', 'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among', 'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry', 'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique', 'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april', 'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor', 'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact', 'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume', 'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction', 'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado', 'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis', 'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball', 'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base', 'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become', 'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt', 'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle', 'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black', 'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood', 'blossom', 'blouse', 'blue', 'blur', 'blush', 'board', 'boat', 'body', 'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring', 'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain', 'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief', 'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother', 'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb', 'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus', 'business', 'busy', 'butter', 'buyer', 'buzz', 'cabbage', 'cabin', 'cable', 'cactus', 'cage', 'cake', 'call', 'calm', 'camera', 'camp', 'can', 'canal', 'cancel', 'candy', 'cannon', 'canoe', 'canvas', 'canyon', 'capable', 'capital', 'captain', 'car', 'carbon', 'card', 'cargo', 'carpet', 'carry', 'cart', 'case', 'cash', 'casino', 'castle', 'casual', 'cat', 'catalog', 'catch', 'category', 'cattle', 'caught', 'cause', 'caution', 'cave', 'ceiling', 'celery', 'cement', 'census', 'century', 'cereal', 'certain', 'chair', 'chalk', 'champion', 'change', 'chaos', 'chapter', 'charge', 'chase', 'chat', 'cheap', 'check', 'cheese', 'chef', 'cherry', 'chest', 'chicken', 'chief', 'child', 'chimney', 'choice', 'choose', 'chronic', 'chuckle', 'chunk', 'churn', 'cigar', 'cinnamon', 'circle', 'citizen', 'city', 'civil', 'claim', 'clap', 'clarify', 'claw', 'clay', 'clean', 'clerk', 'clever', 'click', 'client', 'cliff', 'climb', 'clinic', 'clip', 'clock', 'clog', 'close', 'cloth', 'cloud', 'clown', 'club', 'clump', 'cluster', 'clutch', 'coach', 'coast', 'coconut', 'code', 'coffee', 'coil', 'coin', 'collect', 'color', 'column', 'combine', 'come', 'comfort', 'comic', 'common', 'company', 'concert', 'conduct', 'confirm', 'congress', 'connect', 'consider', 'control', 'convince', 'cook', 'cool', 'copper', 'copy', 'coral', 'core', 'corn', 'correct', 'cost', 'cotton', 'couch', 'country', 'couple', 'course', 'cousin', 'cover', 'coyote', 'crack', 'cradle', 'craft', 'cram', 'crane', 'crash', 'crater', 'crawl', 'crazy', 'cream', 'credit', 'creek', 'crew', 'cricket', 'crime', 'crisp', 'critic', 'crop', 'cross', 'crouch', 'crowd', 'crucial', 'cruel', 'cruise', 'crumble', 'crunch', 'crush', 'cry', 'crystal', 'cube', 'culture', 'cup', 'cupboard', 'curious', 'current', 'curtain', 'curve', 'cushion', 'custom', 'cute', 'cycle', 'dad', 'damage', 'damp', 'dance', 'danger', 'daring', 'dash', 'daughter', 'dawn', 'day', 'deal', 'debate', 'debris', 'decade', 'december', 'decide', 'decline', 'decorate', 'decrease', 'deer', 'defense', 'define', 'defy', 'degree', 'delay', 'deliver', 'demand', 'demise', 'denial', 'dentist', 'deny', 'depart', 'depend', 'deposit', 'depth', 'deputy', 'derive', 'describe', 'desert', 'design', 'desk', 'despair', 'destroy', 'detail', 'detect', 'develop', 'device', 'devote', 'diagram', 'dial', 'diamond', 'diary', 'dice', 'diesel', 'diet', 'differ', 'digital', 'dignity', 'dilemma', 'dinner', 'dinosaur', 'direct', 'dirt', 'disagree', 'discover', 'disease', 'dish', 'dismiss', 'disorder', 'display', 'distance', 'divert', 'divide', 'divorce', 'dizzy', 'doctor', 'document', 'dog', 'doll', 'dolphin', 'domain', 'donate', 'donkey', 'donor', 'door', 'dose', 'double', 'dove', 'draft', 'dragon', 'drama', 'drastic', 'draw', 'dream', 'dress', 'drift', 'drill', 'drink', 'drip', 'drive', 'drop', 'drum', 'dry', 'duck', 'dumb', 'dune', 'during', 'dust', 'dutch', 'duty', 'dwarf', 'dynamic', 'eager', 'eagle', 'early', 'earn', 'earth', 'easily', 'east', 'easy', 'echo', 'ecology', 'economy', 'edge', 'edit', 'educate', 'effort', 'egg', 'eight', 'either', 'elbow', 'elder', 'electric', 'elegant', 'element', 'elephant', 'elevator', 'elite', 'else', 'embark', 'embody', 'embrace', 'emerge', 'emotion', 'employ', 'empower', 'empty', 'enable', 'enact', 'end', 'endless', 'endorse', 'enemy', 'energy', 'enforce', 'engage', 'engine', 'enhance', 'enjoy', 'enlist', 'enough', 'enrich', 'enroll', 'ensure', 'enter', 'entire', 'entry', 'envelope', 'episode', 'equal', 'equip', 'era', 'erase', 'erode', 'erosion', 'error', 'erupt', 'escape', 'essay', 'essence', 'estate', 'eternal', 'ethics', 'evidence', 'evil', 'evoke', 'evolve', 'exact', 'example', 'excess', 'exchange', 'excite', 'exclude', 'excuse', 'execute', 'exercise', 'exhaust', 'exhibit', 'exile', 'exist', 'exit', 'exotic', 'expand', 'expect', 'expire', 'explain', 'expose', 'express', 'extend', 'extra', 'eye', 'eyebrow', 'fabric', 'face', 'faculty', 'fade', 'faint', 'faith', 'fall', 'false', 'fame', 'family', 'famous', 'fan', 'fancy', 'fantasy', 'farm', 'fashion', 'fat', 'fatal', 'father', 'fatigue', 'fault', 'favorite', 'feature', 'february', 'federal', 'fee', 'feed', 'feel', 'female', 'fence', 'festival', 'fetch', 'fever', 'few', 'fiber', 'fiction', 'field', 'figure', 'file', 'film', 'filter', 'final', 'find', 'fine', 'finger', 'finish', 'fire', 'firm', 'first', 'fiscal', 'fish', 'fit', 'fitness', 'fix', 'flag', 'flame', 'flash', 'flat', 'flavor', 'flee', 'flight', 'flip', 'float', 'flock', 'floor', 'flower', 'fluid', 'flush', 'fly', 'foam', 'focus', 'fog', 'foil', 'fold', 'follow', 'food', 'foot', 'force', 'forest', 'forget', 'fork', 'fortune', 'forum', 'forward', 'fossil', 'foster', 'found', 'fox', 'fragile', 'frame', 'frequent', 'fresh', 'friend', 'fringe', 'frog', 'front', 'frost', 'frown', 'frozen', 'fruit', 'fuel', 'fun', 'funny', 'furnace', 'fury', 'future', 'gadget', 'gain', 'galaxy', 'gallery', 'game', 'gap', 'garage', 'garbage', 'garden', 'garlic', 'garment', 'gas', 'gasp', 'gate', 'gather', 'gauge', 'gaze', 'general', 'genius', 'genre', 'gentle', 'genuine', 'gesture', 'ghost', 'giant', 'gift', 'giggle', 'ginger', 'giraffe', 'girl', 'give', 'glad', 'glance', 'glare', 'glass', 'glide', 'glimpse', 'globe', 'gloom', 'glory', 'glove', 'glow', 'glue', 'goat', 'goddess', 'gold', 'good', 'goose', 'gorilla', 'gospel', 'gossip', 'govern', 'gown', 'grab', 'grace', 'grain', 'grant', 'grape', 'grass', 'gravity', 'great', 'green', 'grid', 'grief', 'grit', 'grocery', 'group', 'grow', 'grunt', 'guard', 'guess', 'guide', 'guilt', 'guitar', 'gun', 'gym', 'habit', 'hair', 'half', 'hammer', 'hamster', 'hand', 'happy', 'harbor', 'hard', 'harsh', 'harvest', 'hat', 'have', 'hawk', 'hazard', 'head', 'health', 'heart', 'heavy', 'hedgehog', 'height', 'hello', 'helmet', 'help', 'hen', 'hero', 'hidden', 'high', 'hill', 'hint', 'hip', 'hire', 'history', 'hobby', 'hockey', 'hold', 'hole', 'holiday', 'hollow', 'home', 'honey', 'hood', 'hope', 'horn', 'horror', 'horse', 'hospital', 'host', 'hotel', 'hour', 'hover', 'hub', 'huge', 'human', 'humble', 'humor', 'hundred', 'hungry', 'hunt', 'hurdle', 'hurry', 'hurt', 'husband', 'hybrid', 'ice', 'icon', 'idea', 'identify', 'idle', 'ignore', 'ill', 'illegal', 'illness', 'image', 'imitate', 'immense', 'immune', 'impact', 'impose', 'improve', 'impulse', 'inch', 'include', 'income', 'increase', 'index', 'indicate', 'indoor', 'industry', 'infant', 'inflict', 'inform', 'inhale', 'inherit', 'initial', 'inject', 'injury', 'inmate', 'inner', 'innocent', 'input', 'inquiry', 'insane', 'insect', 'inside', 'inspire', 'install', 'intact', 'interest', 'into', 'invest', 'invite', 'involve', 'iron', 'island', 'isolate', 'issue', 'item', 'ivory', 'jacket', 'jaguar', 'jar', 'jazz', 'jealous', 'jeans', 'jelly', 'jewel', 'job', 'join', 'joke', 'journey', 'joy', 'judge', 'juice', 'jump', 'jungle', 'junior', 'junk', 'just', 'kangaroo', 'keen', 'keep', 'ketchup', 'key', 'kick', 'kid', 'kidney', 'kind', 'kingdom', 'kiss', 'kit', 'kitchen', 'kite', 'kitten', 'kiwi', 'knee', 'knife', 'knock', 'know', 'lab', 'label', 'labor', 'ladder', 'lady', 'lake', 'lamp', 'language', 'laptop', 'large', 'later', 'latin', 'laugh', 'laundry', 'lava', 'law', 'lawn', 'lawsuit', 'layer', 'lazy', 'leader', 'leaf', 'learn', 'leave', 'lecture', 'left', 'leg', 'legal', 'legend', 'leisure', 'lemon', 'lend', 'length', 'lens', 'leopard', 'lesson', 'letter', 'level', 'liar', 'liberty', 'library', 'license', 'life', 'lift', 'light', 'like', 'limb', 'limit', 'link', 'lion', 'liquid', 'list', 'little', 'live', 'lizard', 'load', 'loan', 'lobster', 'local', 'lock', 'logic', 'lonely', 'long', 'loop', 'lottery', 'loud', 'lounge', 'love', 'loyal', 'lucky', 'luggage', 'lumber', 'lunar', 'lunch', 'luxury', 'lyrics', 'machine', 'mad', 'magic', 'magnet', 'maid', 'mail', 'main', 'major', 'make', 'mammal', 'man', 'manage', 'mandate', 'mango', 'mansion', 'manual', 'maple', 'marble', 'march', 'margin', 'marine', 'market', 'marriage', 'mask', 'mass', 'master', 'match', 'material', 'math', 'matrix', 'matter', 'maximum', 'maze', 'meadow', 'mean', 'measure', 'meat', 'mechanic', 'medal', 'media', 'melody', 'melt', 'member', 'memory', 'mention', 'menu', 'mercy', 'merge', 'merit', 'merry', 'mesh', 'message', 'metal', 'method', 'middle', 'midnight', 'milk', 'million', 'mimic', 'mind', 'minimum', 'minor', 'minute', 'miracle', 'mirror', 'misery', 'miss', 'mistake', 'mix', 'mixed', 'mixture', 'mobile', 'model', 'modify', 'mom', 'moment', 'monitor', 'monkey', 'monster', 'month', 'moon', 'moral', 'more', 'morning', 'mosquito', 'mother', 'motion', 'motor', 'mountain', 'mouse', 'move', 'movie', 'much', 'muffin', 'mule', 'multiply', 'muscle', 'museum', 'mushroom', 'music', 'must', 'mutual', 'myself', 'mystery', 'myth', 'naive', 'name', 'napkin', 'narrow', 'nasty', 'nation', 'nature', 'near', 'neck', 'need', 'negative', 'neglect', 'neither', 'nephew', 'nerve', 'nest', 'net', 'network', 'neutral', 'never', 'news', 'next', 'nice', 'night', 'noble', 'noise', 'nominee', 'noodle', 'normal', 'north', 'nose', 'notable', 'note', 'nothing', 'notice', 'novel', 'now', 'nuclear', 'number', 'nurse', 'nut', 'oak', 'obey', 'object', 'oblige', 'obscure', 'observe', 'obtain', 'obvious', 'occur', 'ocean', 'october', 'odor', 'off', 'offer', 'office', 'often', 'oil', 'okay', 'old', 'olive', 'olympic', 'omit', 'once', 'one', 'onion', 'online', 'only', 'open', 'opera', 'opinion', 'oppose', 'option', 'orange', 'orbit', 'orchard', 'order', 'ordinary', 'organ', 'orient', 'original', 'orphan', 'ostrich', 'other', 'outdoor', 'outer', 'output', 'outside', 'oval', 'oven', 'over', 'own', 'owner', 'oxygen', 'oyster', 'ozone', 'pact', 'paddle', 'page', 'pair', 'palace', 'palm', 'panda', 'panel', 'panic', 'panther', 'paper', 'parade', 'parent', 'park', 'parrot', 'party', 'pass', 'patch', 'path', 'patient', 'patrol', 'pattern', 'pause', 'pave', 'payment', 'peace', 'peanut', 'pear', 'peasant', 'pelican', 'pen', 'penalty', 'pencil', 'people', 'pepper', 'perfect', 'permit', 'person', 'pet', 'phone', 'photo', 'phrase', 'physical', 'piano', 'picnic', 'picture', 'piece', 'pig', 'pigeon', 'pill', 'pilot', 'pink', 'pioneer', 'pipe', 'pistol', 'pitch', 'pizza', 'place', 'planet', 'plastic', 'plate', 'play', 'please', 'pledge', 'pluck', 'plug', 'plunge', 'poem', 'poet', 'point', 'polar', 'pole', 'police', 'pond', 'pony', 'pool', 'popular', 'portion', 'position', 'possible', 'post', 'potato', 'pottery', 'poverty', 'powder', 'power', 'practice', 'praise', 'predict', 'prefer', 'prepare', 'present', 'pretty', 'prevent', 'price', 'pride', 'primary', 'print', 'priority', 'prison', 'private', 'prize', 'problem', 'process', 'produce', 'profit', 'program', 'project', 'promote', 'proof', 'property', 'prosper', 'protect', 'proud', 'provide', 'public', 'pudding', 'pull', 'pulp', 'pulse', 'pumpkin', 'punch', 'pupil', 'puppy', 'purchase', 'purity', 'purpose', 'purse', 'push', 'put', 'puzzle', 'pyramid', 'quality', 'quantum', 'quarter', 'question', 'quick', 'quit', 'quiz', 'quote', 'rabbit', 'raccoon', 'race', 'rack', 'radar', 'radio', 'rail', 'rain', 'raise', 'rally', 'ramp', 'ranch', 'random', 'range', 'rapid', 'rare', 'rate', 'rather', 'raven', 'raw', 'razor', 'ready', 'real', 'reason', 'rebel', 'rebuild', 'recall', 'receive', 'recipe', 'record', 'recycle', 'reduce', 'reflect', 'reform', 'refuse', 'region', 'regret', 'regular', 'reject', 'relax', 'release', 'relief', 'rely', 'remain', 'remember', 'remind', 'remove', 'render', 'renew', 'rent', 'reopen', 'repair', 'repeat', 'replace', 'report', 'require', 'rescue', 'resemble', 'resist', 'resource', 'response', 'result', 'retire', 'retreat', 'return', 'reunion', 'reveal', 'review', 'reward', 'rhythm', 'rib', 'ribbon', 'rice', 'rich', 'ride', 'ridge', 'rifle', 'right', 'rigid', 'ring', 'riot', 'ripple', 'risk', 'ritual', 'rival', 'river', 'road', 'roast', 'robot', 'robust', 'rocket', 'romance', 'roof', 'rookie', 'room', 'rose', 'rotate', 'rough', 'round', 'route', 'royal', 'rubber', 'rude', 'rug', 'rule', 'run', 'runway', 'rural', 'sad', 'saddle', 'sadness', 'safe', 'sail', 'salad', 'salmon', 'salon', 'salt', 'salute', 'same', 'sample', 'sand', 'satisfy', 'satoshi', 'sauce', 'sausage', 'save', 'say', 'scale', 'scan', 'scare', 'scatter', 'scene', 'scheme', 'school', 'science', 'scissors', 'scorpion', 'scout', 'scrap', 'screen', 'script', 'scrub', 'sea', 'search', 'season', 'seat', 'second', 'secret', 'section', 'security', 'seed', 'seek', 'segment', 'select', 'sell', 'seminar', 'senior', 'sense', 'sentence', 'series', 'service', 'session', 'settle', 'setup', 'seven', 'shadow', 'shaft', 'shallow', 'share', 'shed', 'shell', 'sheriff', 'shield', 'shift', 'shine', 'ship', 'shiver', 'shock', 'shoe', 'shoot', 'shop', 'short', 'shoulder', 'shove', 'shrimp', 'shrug', 'shuffle', 'shy', 'sibling', 'sick', 'side', 'siege', 'sight', 'sign', 'silent', 'silk', 'silly', 'silver', 'similar', 'simple', 'since', 'sing', 'siren', 'sister', 'situate', 'six', 'size', 'skate', 'sketch', 'ski', 'skill', 'skin', 'skirt', 'skull', 'slab', 'slam', 'sleep', 'slender', 'slice', 'slide', 'slight', 'slim', 'slogan', 'slot', 'slow', 'slush', 'small', 'smart', 'smile', 'smoke', 'smooth', 'snack', 'snake', 'snap', 'sniff', 'snow', 'soap', 'soccer', 'social', 'sock', 'soda', 'soft', 'solar', 'soldier', 'solid', 'solution', 'solve', 'someone', 'song', 'soon', 'sorry', 'sort', 'soul', 'sound', 'soup', 'source', 'south', 'space', 'spare', 'spatial', 'spawn', 'speak', 'special', 'speed', 'spell', 'spend', 'sphere', 'spice', 'spider', 'spike', 'spin', 'spirit', 'split', 'spoil', 'sponsor', 'spoon', 'sport', 'spot', 'spray', 'spread', 'spring', 'spy', 'square', 'squeeze', 'squirrel', 'stable', 'stadium', 'staff', 'stage', 'stairs', 'stamp', 'stand', 'start', 'state', 'stay', 'steak', 'steel', 'stem', 'step', 'stereo', 'stick', 'still', 'sting', 'stock', 'stomach', 'stone', 'stool', 'story', 'stove', 'strategy', 'street', 'strike', 'strong', 'struggle', 'student', 'stuff', 'stumble', 'style', 'subject', 'submit', 'subway', 'success', 'such', 'sudden', 'suffer', 'sugar', 'suggest', 'suit', 'summer', 'sun', 'sunny', 'sunset', 'super', 'supply', 'supreme', 'sure', 'surface', 'surge', 'surprise', 'surround', 'survey', 'suspect', 'sustain', 'swallow', 'swamp', 'swap', 'swarm', 'swear', 'sweet', 'swift', 'swim', 'swing', 'switch', 'sword', 'symbol', 'symptom', 'syrup', 'system', 'table', 'tackle', 'tag', 'tail', 'talent', 'talk', 'tank', 'tape', 'target', 'task', 'taste', 'tattoo', 'taxi', 'teach', 'team', 'tell', 'ten', 'tenant', 'tennis', 'tent', 'term', 'test', 'text', 'thank', 'that', 'theme', 'then', 'theory', 'there', 'they', 'thing', 'this', 'thought', 'three', 'thrive', 'throw', 'thumb', 'thunder', 'ticket', 'tide', 'tiger', 'tilt', 'timber', 'time', 'tiny', 'tip', 'tired', 'tissue', 'title', 'toast', 'tobacco', 'today', 'toddler', 'toe', 'together', 'toilet', 'token', 'tomato', 'tomorrow', 'tone', 'tongue', 'tonight', 'tool', 'tooth', 'top', 'topic', 'topple', 'torch', 'tornado', 'tortoise', 'toss', 'total', 'tourist', 'toward', 'tower', 'town', 'toy', 'track', 'trade', 'traffic', 'tragic', 'train', 'transfer', 'trap', 'trash', 'travel', 'tray', 'treat', 'tree', 'trend', 'trial', 'tribe', 'trick', 'trigger', 'trim', 'trip', 'trophy', 'trouble', 'truck', 'true', 'truly', 'trumpet', 'trust', 'truth', 'try', 'tube', 'tuition', 'tumble', 'tuna', 'tunnel', 'turkey', 'turn', 'turtle', 'twelve', 'twenty', 'twice', 'twin', 'twist', 'two', 'type', 'typical', 'ugly', 'umbrella', 'unable', 'unaware', 'uncle', 'uncover', 'under', 'undo', 'unfair', 'unfold', 'unhappy', 'uniform', 'unique', 'unit', 'universe', 'unknown', 'unlock', 'until', 'unusual', 'unveil', 'update', 'upgrade', 'uphold', 'upon', 'upper', 'upset', 'urban', 'urge', 'usage', 'use', 'used', 'useful', 'useless', 'usual', 'utility', 'vacant', 'vacuum', 'vague', 'valid', 'valley', 'valve', 'van', 'vanish', 'vapor', 'various', 'vast', 'vault', 'vehicle', 'velvet', 'vendor', 'venture', 'venue', 'verb', 'verify', 'version', 'very', 'vessel', 'veteran', 'viable', 'vibrant', 'vicious', 'victory', 'video', 'view', 'village', 'vintage', 'violin', 'virtual', 'virus', 'visa', 'visit', 'visual', 'vital', 'vivid', 'vocal', 'voice', 'void', 'volcano', 'volume', 'vote', 'voyage', 'wage', 'wagon', 'wait', 'walk', 'wall', 'walnut', 'want', 'warfare', 'warm', 'warrior', 'wash', 'wasp', 'waste', 'water', 'wave', 'way', 'wealth', 'weapon', 'wear', 'weasel', 'weather', 'web', 'wedding', 'weekend', 'weird', 'welcome', 'west', 'wet', 'whale', 'what', 'wheat', 'wheel', 'when', 'where', 'whip', 'whisper', 'wide', 'width', 'wife', 'wild', 'will', 'win', 'window', 'wine', 'wing', 'wink', 'winner', 'winter', 'wire', 'wisdom', 'wise', 'wish', 'witness', 'wolf', 'woman', 'wonder', 'wood', 'wool', 'word', 'work', 'world', 'worry', 'worth', 'wrap', 'wreck', 'wrestle', 'wrist', 'write', 'wrong', 'yard', 'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone', 'zoo']) +def key_to_mnemonic(key, wordlist=['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual', 'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance', 'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent', 'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album', 'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone', 'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among', 'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry', 'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique', 'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april', 'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor', 'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact', 'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume', 'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction', 'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado', 'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis', 'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball', 'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base', 'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become', 'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt', 'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle', 'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black', 'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood', 'blossom', 'blouse', 'blue', 'blur', 'blush', 'board', 'boat', 'body', 'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring', 'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain', 'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief', 'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother', 'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb', 'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus', 'business', 'busy', 'butter', 'buyer', 'buzz', 'cabbage', 'cabin', 'cable', 'cactus', 'cage', 'cake', 'call', 'calm', 'camera', 'camp', 'can', 'canal', 'cancel', 'candy', 'cannon', 'canoe', 'canvas', 'canyon', 'capable', 'capital', 'captain', 'car', 'carbon', 'card', 'cargo', 'carpet', 'carry', 'cart', 'case', 'cash', 'casino', 'castle', 'casual', 'cat', 'catalog', 'catch', 'category', 'cattle', 'caught', 'cause', 'caution', 'cave', 'ceiling', 'celery', 'cement', 'census', 'century', 'cereal', 'certain', 'chair', 'chalk', 'champion', 'change', 'chaos', 'chapter', 'charge', 'chase', 'chat', 'cheap', 'check', 'cheese', 'chef', 'cherry', 'chest', 'chicken', 'chief', 'child', 'chimney', 'choice', 'choose', 'chronic', 'chuckle', 'chunk', 'churn', 'cigar', 'cinnamon', 'circle', 'citizen', 'city', 'civil', 'claim', 'clap', 'clarify', 'claw', 'clay', 'clean', 'clerk', 'clever', 'click', 'client', 'cliff', 'climb', 'clinic', 'clip', 'clock', 'clog', 'close', 'cloth', 'cloud', 'clown', 'club', 'clump', 'cluster', 'clutch', 'coach', 'coast', 'coconut', 'code', 'coffee', 'coil', 'coin', 'collect', 'color', 'column', 'combine', 'come', 'comfort', 'comic', 'common', 'company', 'concert', 'conduct', 'confirm', 'congress', 'connect', 'consider', 'control', 'convince', 'cook', 'cool', 'copper', 'copy', 'coral', 'core', 'corn', 'correct', 'cost', 'cotton', 'couch', 'country', 'couple', 'course', 'cousin', 'cover', 'coyote', 'crack', 'cradle', 'craft', 'cram', 'crane', 'crash', 'crater', 'crawl', 'crazy', 'cream', 'credit', 'creek', 'crew', 'cricket', 'crime', 'crisp', 'critic', 'crop', 'cross', 'crouch', 'crowd', 'crucial', 'cruel', 'cruise', 'crumble', 'crunch', 'crush', 'cry', 'crystal', 'cube', 'culture', 'cup', 'cupboard', 'curious', 'current', 'curtain', 'curve', 'cushion', 'custom', 'cute', 'cycle', 'dad', 'damage', 'damp', 'dance', 'danger', 'daring', 'dash', 'daughter', 'dawn', 'day', 'deal', 'debate', 'debris', 'decade', 'december', 'decide', 'decline', 'decorate', 'decrease', 'deer', 'defense', 'define', 'defy', 'degree', 'delay', 'deliver', 'demand', 'demise', 'denial', 'dentist', 'deny', 'depart', 'depend', 'deposit', 'depth', 'deputy', 'derive', 'describe', 'desert', 'design', 'desk', 'despair', 'destroy', 'detail', 'detect', 'develop', 'device', 'devote', 'diagram', 'dial', 'diamond', 'diary', 'dice', 'diesel', 'diet', 'differ', 'digital', 'dignity', 'dilemma', 'dinner', 'dinosaur', 'direct', 'dirt', 'disagree', 'discover', 'disease', 'dish', 'dismiss', 'disorder', 'display', 'distance', 'divert', 'divide', 'divorce', 'dizzy', 'doctor', 'document', 'dog', 'doll', 'dolphin', 'domain', 'donate', 'donkey', 'donor', 'door', 'dose', 'double', 'dove', 'draft', 'dragon', 'drama', 'drastic', 'draw', 'dream', 'dress', 'drift', 'drill', 'drink', 'drip', 'drive', 'drop', 'drum', 'dry', 'duck', 'dumb', 'dune', 'during', 'dust', 'dutch', 'duty', 'dwarf', 'dynamic', 'eager', 'eagle', 'early', 'earn', 'earth', 'easily', 'east', 'easy', 'echo', 'ecology', 'economy', 'edge', 'edit', 'educate', 'effort', 'egg', 'eight', 'either', 'elbow', 'elder', 'electric', 'elegant', 'element', 'elephant', 'elevator', 'elite', 'else', 'embark', 'embody', 'embrace', 'emerge', 'emotion', 'employ', 'empower', 'empty', 'enable', 'enact', 'end', 'endless', 'endorse', 'enemy', 'energy', 'enforce', 'engage', 'engine', 'enhance', 'enjoy', 'enlist', 'enough', 'enrich', 'enroll', 'ensure', 'enter', 'entire', 'entry', 'envelope', 'episode', 'equal', 'equip', 'era', 'erase', 'erode', 'erosion', 'error', 'erupt', 'escape', 'essay', 'essence', 'estate', 'eternal', 'ethics', 'evidence', 'evil', 'evoke', 'evolve', 'exact', 'example', 'excess', 'exchange', 'excite', 'exclude', 'excuse', 'execute', 'exercise', 'exhaust', 'exhibit', 'exile', 'exist', 'exit', 'exotic', 'expand', 'expect', 'expire', 'explain', 'expose', 'express', 'extend', 'extra', 'eye', 'eyebrow', 'fabric', 'face', 'faculty', 'fade', 'faint', 'faith', 'fall', 'false', 'fame', 'family', 'famous', 'fan', 'fancy', 'fantasy', 'farm', 'fashion', 'fat', 'fatal', 'father', 'fatigue', 'fault', 'favorite', 'feature', 'february', 'federal', 'fee', 'feed', 'feel', 'female', 'fence', 'festival', 'fetch', 'fever', 'few', 'fiber', 'fiction', 'field', 'figure', 'file', 'film', 'filter', 'final', 'find', 'fine', 'finger', 'finish', 'fire', 'firm', 'first', 'fiscal', 'fish', 'fit', 'fitness', 'fix', 'flag', 'flame', 'flash', 'flat', 'flavor', 'flee', 'flight', 'flip', 'float', 'flock', 'floor', 'flower', 'fluid', 'flush', 'fly', 'foam', 'focus', 'fog', 'foil', 'fold', 'follow', 'food', 'foot', 'force', 'forest', 'forget', 'fork', 'fortune', 'forum', 'forward', 'fossil', 'foster', 'found', 'fox', 'fragile', 'frame', 'frequent', 'fresh', 'friend', 'fringe', 'frog', 'front', 'frost', 'frown', 'frozen', 'fruit', 'fuel', 'fun', 'funny', 'furnace', 'fury', 'future', 'gadget', 'gain', 'galaxy', 'gallery', 'game', 'gap', 'garage', 'garbage', 'garden', 'garlic', 'garment', 'gas', 'gasp', 'gate', 'gather', 'gauge', 'gaze', 'general', 'genius', 'genre', 'gentle', 'genuine', 'gesture', 'ghost', 'giant', 'gift', 'giggle', 'ginger', 'giraffe', 'girl', 'give', 'glad', 'glance', 'glare', 'glass', 'glide', 'glimpse', 'globe', 'gloom', 'glory', 'glove', 'glow', 'glue', 'goat', 'goddess', 'gold', 'good', 'goose', 'gorilla', 'gospel', 'gossip', 'govern', 'gown', 'grab', 'grace', 'grain', 'grant', 'grape', 'grass', 'gravity', 'great', 'green', 'grid', 'grief', 'grit', 'grocery', 'group', 'grow', 'grunt', 'guard', 'guess', 'guide', 'guilt', 'guitar', 'gun', 'gym', 'habit', 'hair', 'half', 'hammer', 'hamster', 'hand', 'happy', 'harbor', 'hard', 'harsh', 'harvest', 'hat', 'have', 'hawk', 'hazard', 'head', 'health', 'heart', 'heavy', 'hedgehog', 'height', 'hello', 'helmet', 'help', 'hen', 'hero', 'hidden', 'high', 'hill', 'hint', 'hip', 'hire', 'history', 'hobby', 'hockey', 'hold', 'hole', 'holiday', 'hollow', 'home', 'honey', 'hood', 'hope', 'horn', 'horror', 'horse', 'hospital', 'host', 'hotel', 'hour', 'hover', 'hub', 'huge', 'human', 'humble', 'humor', 'hundred', 'hungry', 'hunt', 'hurdle', 'hurry', 'hurt', 'husband', 'hybrid', 'ice', 'icon', 'idea', 'identify', 'idle', 'ignore', 'ill', 'illegal', 'illness', 'image', 'imitate', 'immense', 'immune', 'impact', 'impose', 'improve', 'impulse', 'inch', 'include', 'income', 'increase', 'index', 'indicate', 'indoor', 'industry', 'infant', 'inflict', 'inform', 'inhale', 'inherit', 'initial', 'inject', 'injury', 'inmate', 'inner', 'innocent', 'input', 'inquiry', 'insane', 'insect', 'inside', 'inspire', 'install', 'intact', 'interest', 'into', 'invest', 'invite', 'involve', 'iron', 'island', 'isolate', 'issue', 'item', 'ivory', 'jacket', 'jaguar', 'jar', 'jazz', 'jealous', 'jeans', 'jelly', 'jewel', 'job', 'join', 'joke', 'journey', 'joy', 'judge', 'juice', 'jump', 'jungle', 'junior', 'junk', 'just', 'kangaroo', 'keen', 'keep', 'ketchup', 'key', 'kick', 'kid', 'kidney', 'kind', 'kingdom', 'kiss', 'kit', 'kitchen', 'kite', 'kitten', 'kiwi', 'knee', 'knife', 'knock', 'know', 'lab', 'label', 'labor', 'ladder', 'lady', 'lake', 'lamp', 'language', 'laptop', 'large', 'later', 'latin', 'laugh', 'laundry', 'lava', 'law', 'lawn', 'lawsuit', 'layer', 'lazy', 'leader', 'leaf', 'learn', 'leave', 'lecture', 'left', 'leg', 'legal', 'legend', 'leisure', 'lemon', 'lend', 'length', 'lens', 'leopard', 'lesson', 'letter', 'level', 'liar', 'liberty', 'library', 'license', 'life', 'lift', 'light', 'like', 'limb', 'limit', 'link', 'lion', 'liquid', 'list', 'little', 'live', 'lizard', 'load', 'loan', 'lobster', 'local', 'lock', 'logic', 'lonely', 'long', 'loop', 'lottery', 'loud', 'lounge', 'love', 'loyal', 'lucky', 'luggage', 'lumber', 'lunar', 'lunch', 'luxury', 'lyrics', 'machine', 'mad', 'magic', 'magnet', 'maid', 'mail', 'main', 'major', 'make', 'mammal', 'man', 'manage', 'mandate', 'mango', 'mansion', 'manual', 'maple', 'marble', 'march', 'margin', 'marine', 'market', 'marriage', 'mask', 'mass', 'master', 'match', 'material', 'math', 'matrix', 'matter', 'maximum', 'maze', 'meadow', 'mean', 'measure', 'meat', 'mechanic', 'medal', 'media', 'melody', 'melt', 'member', 'memory', 'mention', 'menu', 'mercy', 'merge', 'merit', 'merry', 'mesh', 'message', 'metal', 'method', 'middle', 'midnight', 'milk', 'million', 'mimic', 'mind', 'minimum', 'minor', 'minute', 'miracle', 'mirror', 'misery', 'miss', 'mistake', 'mix', 'mixed', 'mixture', 'mobile', 'model', 'modify', 'mom', 'moment', 'monitor', 'monkey', 'monster', 'month', 'moon', 'moral', 'more', 'morning', 'mosquito', 'mother', 'motion', 'motor', 'mountain', 'mouse', 'move', 'movie', 'much', 'muffin', 'mule', 'multiply', 'muscle', 'museum', 'mushroom', 'music', 'must', 'mutual', 'myself', 'mystery', 'myth', 'naive', 'name', 'napkin', 'narrow', 'nasty', 'nation', 'nature', 'near', 'neck', 'need', 'negative', 'neglect', 'neither', 'nephew', 'nerve', 'nest', 'net', 'network', 'neutral', 'never', 'news', 'next', 'nice', 'night', 'noble', 'noise', 'nominee', 'noodle', 'normal', 'north', 'nose', 'notable', 'note', 'nothing', 'notice', 'novel', 'now', 'nuclear', 'number', 'nurse', 'nut', 'oak', 'obey', 'object', 'oblige', 'obscure', 'observe', 'obtain', 'obvious', 'occur', 'ocean', 'october', 'odor', 'off', 'offer', 'office', 'often', 'oil', 'okay', 'old', 'olive', 'olympic', 'omit', 'once', 'one', 'onion', 'online', 'only', 'open', 'opera', 'opinion', 'oppose', 'option', 'orange', 'orbit', 'orchard', 'order', 'ordinary', 'organ', 'orient', 'original', 'orphan', 'ostrich', 'other', 'outdoor', 'outer', 'output', 'outside', 'oval', 'oven', 'over', 'own', 'owner', 'oxygen', 'oyster', 'ozone', 'pact', 'paddle', 'page', 'pair', 'palace', 'palm', 'panda', 'panel', 'panic', 'panther', 'paper', 'parade', 'parent', 'park', 'parrot', 'party', 'pass', 'patch', 'path', 'patient', 'patrol', 'pattern', 'pause', 'pave', 'payment', 'peace', 'peanut', 'pear', 'peasant', 'pelican', 'pen', 'penalty', 'pencil', 'people', 'pepper', 'perfect', 'permit', 'person', 'pet', 'phone', 'photo', 'phrase', 'physical', 'piano', 'picnic', 'picture', 'piece', 'pig', 'pigeon', 'pill', 'pilot', 'pink', 'pioneer', 'pipe', 'pistol', 'pitch', 'pizza', 'place', 'planet', 'plastic', 'plate', 'play', 'please', 'pledge', 'pluck', 'plug', 'plunge', 'poem', 'poet', 'point', 'polar', 'pole', 'police', 'pond', 'pony', 'pool', 'popular', 'portion', 'position', 'possible', 'post', 'potato', 'pottery', 'poverty', 'powder', 'power', 'practice', 'praise', 'predict', 'prefer', 'prepare', 'present', 'pretty', 'prevent', 'price', 'pride', 'primary', 'print', 'priority', 'prison', 'private', 'prize', 'problem', 'process', 'produce', 'profit', 'program', 'project', 'promote', 'proof', 'property', 'prosper', 'protect', 'proud', 'provide', 'public', 'pudding', 'pull', 'pulp', 'pulse', 'pumpkin', 'punch', 'pupil', 'puppy', 'purchase', 'purity', 'purpose', 'purse', 'push', 'put', 'puzzle', 'pyramid', 'quality', 'quantum', 'quarter', 'question', 'quick', 'quit', 'quiz', 'quote', 'rabbit', 'raccoon', 'race', 'rack', 'radar', 'radio', 'rail', 'rain', 'raise', 'rally', 'ramp', 'ranch', 'random', 'range', 'rapid', 'rare', 'rate', 'rather', 'raven', 'raw', 'razor', 'ready', 'real', 'reason', 'rebel', 'rebuild', 'recall', 'receive', 'recipe', 'record', 'recycle', 'reduce', 'reflect', 'reform', 'refuse', 'region', 'regret', 'regular', 'reject', 'relax', 'release', 'relief', 'rely', 'remain', 'remember', 'remind', 'remove', 'render', 'renew', 'rent', 'reopen', 'repair', 'repeat', 'replace', 'report', 'require', 'rescue', 'resemble', 'resist', 'resource', 'response', 'result', 'retire', 'retreat', 'return', 'reunion', 'reveal', 'review', 'reward', 'rhythm', 'rib', 'ribbon', 'rice', 'rich', 'ride', 'ridge', 'rifle', 'right', 'rigid', 'ring', 'riot', 'ripple', 'risk', 'ritual', 'rival', 'river', 'road', 'roast', 'robot', 'robust', 'rocket', 'romance', 'roof', 'rookie', 'room', 'rose', 'rotate', 'rough', 'round', 'route', 'royal', 'rubber', 'rude', 'rug', 'rule', 'run', 'runway', 'rural', 'sad', 'saddle', 'sadness', 'safe', 'sail', 'salad', 'salmon', 'salon', 'salt', 'salute', 'same', 'sample', 'sand', 'satisfy', 'satoshi', 'sauce', 'sausage', 'save', 'say', 'scale', 'scan', 'scare', 'scatter', 'scene', 'scheme', 'school', 'science', 'scissors', 'scorpion', 'scout', 'scrap', 'screen', 'script', 'scrub', 'sea', 'search', 'season', 'seat', 'second', 'secret', 'section', 'security', 'seed', 'seek', 'segment', 'select', 'sell', 'seminar', 'senior', 'sense', 'sentence', 'series', 'service', 'session', 'settle', 'setup', 'seven', 'shadow', 'shaft', 'shallow', 'share', 'shed', 'shell', 'sheriff', 'shield', 'shift', 'shine', 'ship', 'shiver', 'shock', 'shoe', 'shoot', 'shop', 'short', 'shoulder', 'shove', 'shrimp', 'shrug', 'shuffle', 'shy', 'sibling', 'sick', 'side', 'siege', 'sight', 'sign', 'silent', 'silk', 'silly', 'silver', 'similar', 'simple', 'since', 'sing', 'siren', 'sister', 'situate', 'six', 'size', 'skate', 'sketch', 'ski', 'skill', 'skin', 'skirt', 'skull', 'slab', 'slam', 'sleep', 'slender', 'slice', 'slide', 'slight', 'slim', 'slogan', 'slot', 'slow', 'slush', 'small', 'smart', 'smile', 'smoke', 'smooth', 'snack', 'snake', 'snap', 'sniff', 'snow', 'soap', 'soccer', 'social', 'sock', 'soda', 'soft', 'solar', 'soldier', 'solid', 'solution', 'solve', 'someone', 'song', 'soon', 'sorry', 'sort', 'soul', 'sound', 'soup', 'source', 'south', 'space', 'spare', 'spatial', 'spawn', 'speak', 'special', 'speed', 'spell', 'spend', 'sphere', 'spice', 'spider', 'spike', 'spin', 'spirit', 'split', 'spoil', 'sponsor', 'spoon', 'sport', 'spot', 'spray', 'spread', 'spring', 'spy', 'square', 'squeeze', 'squirrel', 'stable', 'stadium', 'staff', 'stage', 'stairs', 'stamp', 'stand', 'start', 'state', 'stay', 'steak', 'steel', 'stem', 'step', 'stereo', 'stick', 'still', 'sting', 'stock', 'stomach', 'stone', 'stool', 'story', 'stove', 'strategy', 'street', 'strike', 'strong', 'struggle', 'student', 'stuff', 'stumble', 'style', 'subject', 'submit', 'subway', 'success', 'such', 'sudden', 'suffer', 'sugar', 'suggest', 'suit', 'summer', 'sun', 'sunny', 'sunset', 'super', 'supply', 'supreme', 'sure', 'surface', 'surge', 'surprise', 'surround', 'survey', 'suspect', 'sustain', 'swallow', 'swamp', 'swap', 'swarm', 'swear', 'sweet', 'swift', 'swim', 'swing', 'switch', 'sword', 'symbol', 'symptom', 'syrup', 'system', 'table', 'tackle', 'tag', 'tail', 'talent', 'talk', 'tank', 'tape', 'target', 'task', 'taste', 'tattoo', 'taxi', 'teach', 'team', 'tell', 'ten', 'tenant', 'tennis', 'tent', 'term', 'test', 'text', 'thank', 'that', 'theme', 'then', 'theory', 'there', 'they', 'thing', 'this', 'thought', 'three', 'thrive', 'throw', 'thumb', 'thunder', 'ticket', 'tide', 'tiger', 'tilt', 'timber', 'time', 'tiny', 'tip', 'tired', 'tissue', 'title', 'toast', 'tobacco', 'today', 'toddler', 'toe', 'together', 'toilet', 'token', 'tomato', 'tomorrow', 'tone', 'tongue', 'tonight', 'tool', 'tooth', 'top', 'topic', 'topple', 'torch', 'tornado', 'tortoise', 'toss', 'total', 'tourist', 'toward', 'tower', 'town', 'toy', 'track', 'trade', 'traffic', 'tragic', 'train', 'transfer', 'trap', 'trash', 'travel', 'tray', 'treat', 'tree', 'trend', 'trial', 'tribe', 'trick', 'trigger', 'trim', 'trip', 'trophy', 'trouble', 'truck', 'true', 'truly', 'trumpet', 'trust', 'truth', 'try', 'tube', 'tuition', 'tumble', 'tuna', 'tunnel', 'turkey', 'turn', 'turtle', 'twelve', 'twenty', 'twice', 'twin', 'twist', 'two', 'type', 'typical', 'ugly', 'umbrella', 'unable', 'unaware', 'uncle', 'uncover', 'under', 'undo', 'unfair', 'unfold', 'unhappy', 'uniform', 'unique', 'unit', 'universe', 'unknown', 'unlock', 'until', 'unusual', 'unveil', 'update', 'upgrade', 'uphold', 'upon', 'upper', 'upset', 'urban', 'urge', 'usage', 'use', 'used', 'useful', 'useless', 'usual', 'utility', 'vacant', 'vacuum', 'vague', 'valid', 'valley', 'valve', 'van', 'vanish', 'vapor', 'various', 'vast', 'vault', 'vehicle', 'velvet', 'vendor', 'venture', 'venue', 'verb', 'verify', 'version', 'very', 'vessel', 'veteran', 'viable', 'vibrant', 'vicious', 'victory', 'video', 'view', 'village', 'vintage', 'violin', 'virtual', 'virus', 'visa', 'visit', 'visual', 'vital', 'vivid', 'vocal', 'voice', 'void', 'volcano', 'volume', 'vote', 'voyage', 'wage', 'wagon', 'wait', 'walk', 'wall', 'walnut', 'want', 'warfare', 'warm', 'warrior', 'wash', 'wasp', 'waste', 'water', 'wave', 'way', 'wealth', 'weapon', 'wear', 'weasel', 'weather', 'web', 'wedding', 'weekend', 'weird', 'welcome', 'west', 'wet', 'whale', 'what', 'wheat', 'wheel', 'when', 'where', 'whip', 'whisper', 'wide', 'width', 'wife', 'wild', 'will', 'win', 'window', 'wine', 'wing', 'wink', 'winner', 'winter', 'wire', 'wisdom', 'wise', 'wish', 'witness', 'wolf', 'woman', 'wonder', 'wood', 'wool', 'word', 'work', 'world', 'worry', 'worth', 'wrap', 'wreck', 'wrestle', 'wrist', 'write', 'wrong', 'yard', 'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone', 'zoo'])
-

Convert the passed key to memorizable mnemonic.

+

Convert the passed key to memorizable mnemonic.

Args

key : bytes
The key to be encoded.
-
wordlist : list[str], optional
+
wordlist : list[str], optional
The wordlist used to generate the mnemonic. Defaults to wordlist.

Returns

-
str
+
str
A string of space separated words representing the mnemonic
-
+
-Source code + +Expand source code +
def key_to_mnemonic(key, wordlist=wordlist):
     """Convert the passed key to memorizable mnemonic.
 
@@ -274,29 +286,31 @@ 

Returns

-def mnemonic_to_key(mnemonic, wordlist=['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual', 'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance', 'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent', 'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album', 'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone', 'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among', 'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry', 'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique', 'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april', 'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor', 'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact', 'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume', 'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction', 'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado', 'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis', 'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball', 'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base', 'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become', 'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt', 'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle', 'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black', 'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood', 'blossom', 'blouse', 'blue', 'blur', 'blush', 'board', 'boat', 'body', 'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring', 'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain', 'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief', 'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother', 'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb', 'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus', 'business', 'busy', 'butter', 'buyer', 'buzz', 'cabbage', 'cabin', 'cable', 'cactus', 'cage', 'cake', 'call', 'calm', 'camera', 'camp', 'can', 'canal', 'cancel', 'candy', 'cannon', 'canoe', 'canvas', 'canyon', 'capable', 'capital', 'captain', 'car', 'carbon', 'card', 'cargo', 'carpet', 'carry', 'cart', 'case', 'cash', 'casino', 'castle', 'casual', 'cat', 'catalog', 'catch', 'category', 'cattle', 'caught', 'cause', 'caution', 'cave', 'ceiling', 'celery', 'cement', 'census', 'century', 'cereal', 'certain', 'chair', 'chalk', 'champion', 'change', 'chaos', 'chapter', 'charge', 'chase', 'chat', 'cheap', 'check', 'cheese', 'chef', 'cherry', 'chest', 'chicken', 'chief', 'child', 'chimney', 'choice', 'choose', 'chronic', 'chuckle', 'chunk', 'churn', 'cigar', 'cinnamon', 'circle', 'citizen', 'city', 'civil', 'claim', 'clap', 'clarify', 'claw', 'clay', 'clean', 'clerk', 'clever', 'click', 'client', 'cliff', 'climb', 'clinic', 'clip', 'clock', 'clog', 'close', 'cloth', 'cloud', 'clown', 'club', 'clump', 'cluster', 'clutch', 'coach', 'coast', 'coconut', 'code', 'coffee', 'coil', 'coin', 'collect', 'color', 'column', 'combine', 'come', 'comfort', 'comic', 'common', 'company', 'concert', 'conduct', 'confirm', 'congress', 'connect', 'consider', 'control', 'convince', 'cook', 'cool', 'copper', 'copy', 'coral', 'core', 'corn', 'correct', 'cost', 'cotton', 'couch', 'country', 'couple', 'course', 'cousin', 'cover', 'coyote', 'crack', 'cradle', 'craft', 'cram', 'crane', 'crash', 'crater', 'crawl', 'crazy', 'cream', 'credit', 'creek', 'crew', 'cricket', 'crime', 'crisp', 'critic', 'crop', 'cross', 'crouch', 'crowd', 'crucial', 'cruel', 'cruise', 'crumble', 'crunch', 'crush', 'cry', 'crystal', 'cube', 'culture', 'cup', 'cupboard', 'curious', 'current', 'curtain', 'curve', 'cushion', 'custom', 'cute', 'cycle', 'dad', 'damage', 'damp', 'dance', 'danger', 'daring', 'dash', 'daughter', 'dawn', 'day', 'deal', 'debate', 'debris', 'decade', 'december', 'decide', 'decline', 'decorate', 'decrease', 'deer', 'defense', 'define', 'defy', 'degree', 'delay', 'deliver', 'demand', 'demise', 'denial', 'dentist', 'deny', 'depart', 'depend', 'deposit', 'depth', 'deputy', 'derive', 'describe', 'desert', 'design', 'desk', 'despair', 'destroy', 'detail', 'detect', 'develop', 'device', 'devote', 'diagram', 'dial', 'diamond', 'diary', 'dice', 'diesel', 'diet', 'differ', 'digital', 'dignity', 'dilemma', 'dinner', 'dinosaur', 'direct', 'dirt', 'disagree', 'discover', 'disease', 'dish', 'dismiss', 'disorder', 'display', 'distance', 'divert', 'divide', 'divorce', 'dizzy', 'doctor', 'document', 'dog', 'doll', 'dolphin', 'domain', 'donate', 'donkey', 'donor', 'door', 'dose', 'double', 'dove', 'draft', 'dragon', 'drama', 'drastic', 'draw', 'dream', 'dress', 'drift', 'drill', 'drink', 'drip', 'drive', 'drop', 'drum', 'dry', 'duck', 'dumb', 'dune', 'during', 'dust', 'dutch', 'duty', 'dwarf', 'dynamic', 'eager', 'eagle', 'early', 'earn', 'earth', 'easily', 'east', 'easy', 'echo', 'ecology', 'economy', 'edge', 'edit', 'educate', 'effort', 'egg', 'eight', 'either', 'elbow', 'elder', 'electric', 'elegant', 'element', 'elephant', 'elevator', 'elite', 'else', 'embark', 'embody', 'embrace', 'emerge', 'emotion', 'employ', 'empower', 'empty', 'enable', 'enact', 'end', 'endless', 'endorse', 'enemy', 'energy', 'enforce', 'engage', 'engine', 'enhance', 'enjoy', 'enlist', 'enough', 'enrich', 'enroll', 'ensure', 'enter', 'entire', 'entry', 'envelope', 'episode', 'equal', 'equip', 'era', 'erase', 'erode', 'erosion', 'error', 'erupt', 'escape', 'essay', 'essence', 'estate', 'eternal', 'ethics', 'evidence', 'evil', 'evoke', 'evolve', 'exact', 'example', 'excess', 'exchange', 'excite', 'exclude', 'excuse', 'execute', 'exercise', 'exhaust', 'exhibit', 'exile', 'exist', 'exit', 'exotic', 'expand', 'expect', 'expire', 'explain', 'expose', 'express', 'extend', 'extra', 'eye', 'eyebrow', 'fabric', 'face', 'faculty', 'fade', 'faint', 'faith', 'fall', 'false', 'fame', 'family', 'famous', 'fan', 'fancy', 'fantasy', 'farm', 'fashion', 'fat', 'fatal', 'father', 'fatigue', 'fault', 'favorite', 'feature', 'february', 'federal', 'fee', 'feed', 'feel', 'female', 'fence', 'festival', 'fetch', 'fever', 'few', 'fiber', 'fiction', 'field', 'figure', 'file', 'film', 'filter', 'final', 'find', 'fine', 'finger', 'finish', 'fire', 'firm', 'first', 'fiscal', 'fish', 'fit', 'fitness', 'fix', 'flag', 'flame', 'flash', 'flat', 'flavor', 'flee', 'flight', 'flip', 'float', 'flock', 'floor', 'flower', 'fluid', 'flush', 'fly', 'foam', 'focus', 'fog', 'foil', 'fold', 'follow', 'food', 'foot', 'force', 'forest', 'forget', 'fork', 'fortune', 'forum', 'forward', 'fossil', 'foster', 'found', 'fox', 'fragile', 'frame', 'frequent', 'fresh', 'friend', 'fringe', 'frog', 'front', 'frost', 'frown', 'frozen', 'fruit', 'fuel', 'fun', 'funny', 'furnace', 'fury', 'future', 'gadget', 'gain', 'galaxy', 'gallery', 'game', 'gap', 'garage', 'garbage', 'garden', 'garlic', 'garment', 'gas', 'gasp', 'gate', 'gather', 'gauge', 'gaze', 'general', 'genius', 'genre', 'gentle', 'genuine', 'gesture', 'ghost', 'giant', 'gift', 'giggle', 'ginger', 'giraffe', 'girl', 'give', 'glad', 'glance', 'glare', 'glass', 'glide', 'glimpse', 'globe', 'gloom', 'glory', 'glove', 'glow', 'glue', 'goat', 'goddess', 'gold', 'good', 'goose', 'gorilla', 'gospel', 'gossip', 'govern', 'gown', 'grab', 'grace', 'grain', 'grant', 'grape', 'grass', 'gravity', 'great', 'green', 'grid', 'grief', 'grit', 'grocery', 'group', 'grow', 'grunt', 'guard', 'guess', 'guide', 'guilt', 'guitar', 'gun', 'gym', 'habit', 'hair', 'half', 'hammer', 'hamster', 'hand', 'happy', 'harbor', 'hard', 'harsh', 'harvest', 'hat', 'have', 'hawk', 'hazard', 'head', 'health', 'heart', 'heavy', 'hedgehog', 'height', 'hello', 'helmet', 'help', 'hen', 'hero', 'hidden', 'high', 'hill', 'hint', 'hip', 'hire', 'history', 'hobby', 'hockey', 'hold', 'hole', 'holiday', 'hollow', 'home', 'honey', 'hood', 'hope', 'horn', 'horror', 'horse', 'hospital', 'host', 'hotel', 'hour', 'hover', 'hub', 'huge', 'human', 'humble', 'humor', 'hundred', 'hungry', 'hunt', 'hurdle', 'hurry', 'hurt', 'husband', 'hybrid', 'ice', 'icon', 'idea', 'identify', 'idle', 'ignore', 'ill', 'illegal', 'illness', 'image', 'imitate', 'immense', 'immune', 'impact', 'impose', 'improve', 'impulse', 'inch', 'include', 'income', 'increase', 'index', 'indicate', 'indoor', 'industry', 'infant', 'inflict', 'inform', 'inhale', 'inherit', 'initial', 'inject', 'injury', 'inmate', 'inner', 'innocent', 'input', 'inquiry', 'insane', 'insect', 'inside', 'inspire', 'install', 'intact', 'interest', 'into', 'invest', 'invite', 'involve', 'iron', 'island', 'isolate', 'issue', 'item', 'ivory', 'jacket', 'jaguar', 'jar', 'jazz', 'jealous', 'jeans', 'jelly', 'jewel', 'job', 'join', 'joke', 'journey', 'joy', 'judge', 'juice', 'jump', 'jungle', 'junior', 'junk', 'just', 'kangaroo', 'keen', 'keep', 'ketchup', 'key', 'kick', 'kid', 'kidney', 'kind', 'kingdom', 'kiss', 'kit', 'kitchen', 'kite', 'kitten', 'kiwi', 'knee', 'knife', 'knock', 'know', 'lab', 'label', 'labor', 'ladder', 'lady', 'lake', 'lamp', 'language', 'laptop', 'large', 'later', 'latin', 'laugh', 'laundry', 'lava', 'law', 'lawn', 'lawsuit', 'layer', 'lazy', 'leader', 'leaf', 'learn', 'leave', 'lecture', 'left', 'leg', 'legal', 'legend', 'leisure', 'lemon', 'lend', 'length', 'lens', 'leopard', 'lesson', 'letter', 'level', 'liar', 'liberty', 'library', 'license', 'life', 'lift', 'light', 'like', 'limb', 'limit', 'link', 'lion', 'liquid', 'list', 'little', 'live', 'lizard', 'load', 'loan', 'lobster', 'local', 'lock', 'logic', 'lonely', 'long', 'loop', 'lottery', 'loud', 'lounge', 'love', 'loyal', 'lucky', 'luggage', 'lumber', 'lunar', 'lunch', 'luxury', 'lyrics', 'machine', 'mad', 'magic', 'magnet', 'maid', 'mail', 'main', 'major', 'make', 'mammal', 'man', 'manage', 'mandate', 'mango', 'mansion', 'manual', 'maple', 'marble', 'march', 'margin', 'marine', 'market', 'marriage', 'mask', 'mass', 'master', 'match', 'material', 'math', 'matrix', 'matter', 'maximum', 'maze', 'meadow', 'mean', 'measure', 'meat', 'mechanic', 'medal', 'media', 'melody', 'melt', 'member', 'memory', 'mention', 'menu', 'mercy', 'merge', 'merit', 'merry', 'mesh', 'message', 'metal', 'method', 'middle', 'midnight', 'milk', 'million', 'mimic', 'mind', 'minimum', 'minor', 'minute', 'miracle', 'mirror', 'misery', 'miss', 'mistake', 'mix', 'mixed', 'mixture', 'mobile', 'model', 'modify', 'mom', 'moment', 'monitor', 'monkey', 'monster', 'month', 'moon', 'moral', 'more', 'morning', 'mosquito', 'mother', 'motion', 'motor', 'mountain', 'mouse', 'move', 'movie', 'much', 'muffin', 'mule', 'multiply', 'muscle', 'museum', 'mushroom', 'music', 'must', 'mutual', 'myself', 'mystery', 'myth', 'naive', 'name', 'napkin', 'narrow', 'nasty', 'nation', 'nature', 'near', 'neck', 'need', 'negative', 'neglect', 'neither', 'nephew', 'nerve', 'nest', 'net', 'network', 'neutral', 'never', 'news', 'next', 'nice', 'night', 'noble', 'noise', 'nominee', 'noodle', 'normal', 'north', 'nose', 'notable', 'note', 'nothing', 'notice', 'novel', 'now', 'nuclear', 'number', 'nurse', 'nut', 'oak', 'obey', 'object', 'oblige', 'obscure', 'observe', 'obtain', 'obvious', 'occur', 'ocean', 'october', 'odor', 'off', 'offer', 'office', 'often', 'oil', 'okay', 'old', 'olive', 'olympic', 'omit', 'once', 'one', 'onion', 'online', 'only', 'open', 'opera', 'opinion', 'oppose', 'option', 'orange', 'orbit', 'orchard', 'order', 'ordinary', 'organ', 'orient', 'original', 'orphan', 'ostrich', 'other', 'outdoor', 'outer', 'output', 'outside', 'oval', 'oven', 'over', 'own', 'owner', 'oxygen', 'oyster', 'ozone', 'pact', 'paddle', 'page', 'pair', 'palace', 'palm', 'panda', 'panel', 'panic', 'panther', 'paper', 'parade', 'parent', 'park', 'parrot', 'party', 'pass', 'patch', 'path', 'patient', 'patrol', 'pattern', 'pause', 'pave', 'payment', 'peace', 'peanut', 'pear', 'peasant', 'pelican', 'pen', 'penalty', 'pencil', 'people', 'pepper', 'perfect', 'permit', 'person', 'pet', 'phone', 'photo', 'phrase', 'physical', 'piano', 'picnic', 'picture', 'piece', 'pig', 'pigeon', 'pill', 'pilot', 'pink', 'pioneer', 'pipe', 'pistol', 'pitch', 'pizza', 'place', 'planet', 'plastic', 'plate', 'play', 'please', 'pledge', 'pluck', 'plug', 'plunge', 'poem', 'poet', 'point', 'polar', 'pole', 'police', 'pond', 'pony', 'pool', 'popular', 'portion', 'position', 'possible', 'post', 'potato', 'pottery', 'poverty', 'powder', 'power', 'practice', 'praise', 'predict', 'prefer', 'prepare', 'present', 'pretty', 'prevent', 'price', 'pride', 'primary', 'print', 'priority', 'prison', 'private', 'prize', 'problem', 'process', 'produce', 'profit', 'program', 'project', 'promote', 'proof', 'property', 'prosper', 'protect', 'proud', 'provide', 'public', 'pudding', 'pull', 'pulp', 'pulse', 'pumpkin', 'punch', 'pupil', 'puppy', 'purchase', 'purity', 'purpose', 'purse', 'push', 'put', 'puzzle', 'pyramid', 'quality', 'quantum', 'quarter', 'question', 'quick', 'quit', 'quiz', 'quote', 'rabbit', 'raccoon', 'race', 'rack', 'radar', 'radio', 'rail', 'rain', 'raise', 'rally', 'ramp', 'ranch', 'random', 'range', 'rapid', 'rare', 'rate', 'rather', 'raven', 'raw', 'razor', 'ready', 'real', 'reason', 'rebel', 'rebuild', 'recall', 'receive', 'recipe', 'record', 'recycle', 'reduce', 'reflect', 'reform', 'refuse', 'region', 'regret', 'regular', 'reject', 'relax', 'release', 'relief', 'rely', 'remain', 'remember', 'remind', 'remove', 'render', 'renew', 'rent', 'reopen', 'repair', 'repeat', 'replace', 'report', 'require', 'rescue', 'resemble', 'resist', 'resource', 'response', 'result', 'retire', 'retreat', 'return', 'reunion', 'reveal', 'review', 'reward', 'rhythm', 'rib', 'ribbon', 'rice', 'rich', 'ride', 'ridge', 'rifle', 'right', 'rigid', 'ring', 'riot', 'ripple', 'risk', 'ritual', 'rival', 'river', 'road', 'roast', 'robot', 'robust', 'rocket', 'romance', 'roof', 'rookie', 'room', 'rose', 'rotate', 'rough', 'round', 'route', 'royal', 'rubber', 'rude', 'rug', 'rule', 'run', 'runway', 'rural', 'sad', 'saddle', 'sadness', 'safe', 'sail', 'salad', 'salmon', 'salon', 'salt', 'salute', 'same', 'sample', 'sand', 'satisfy', 'satoshi', 'sauce', 'sausage', 'save', 'say', 'scale', 'scan', 'scare', 'scatter', 'scene', 'scheme', 'school', 'science', 'scissors', 'scorpion', 'scout', 'scrap', 'screen', 'script', 'scrub', 'sea', 'search', 'season', 'seat', 'second', 'secret', 'section', 'security', 'seed', 'seek', 'segment', 'select', 'sell', 'seminar', 'senior', 'sense', 'sentence', 'series', 'service', 'session', 'settle', 'setup', 'seven', 'shadow', 'shaft', 'shallow', 'share', 'shed', 'shell', 'sheriff', 'shield', 'shift', 'shine', 'ship', 'shiver', 'shock', 'shoe', 'shoot', 'shop', 'short', 'shoulder', 'shove', 'shrimp', 'shrug', 'shuffle', 'shy', 'sibling', 'sick', 'side', 'siege', 'sight', 'sign', 'silent', 'silk', 'silly', 'silver', 'similar', 'simple', 'since', 'sing', 'siren', 'sister', 'situate', 'six', 'size', 'skate', 'sketch', 'ski', 'skill', 'skin', 'skirt', 'skull', 'slab', 'slam', 'sleep', 'slender', 'slice', 'slide', 'slight', 'slim', 'slogan', 'slot', 'slow', 'slush', 'small', 'smart', 'smile', 'smoke', 'smooth', 'snack', 'snake', 'snap', 'sniff', 'snow', 'soap', 'soccer', 'social', 'sock', 'soda', 'soft', 'solar', 'soldier', 'solid', 'solution', 'solve', 'someone', 'song', 'soon', 'sorry', 'sort', 'soul', 'sound', 'soup', 'source', 'south', 'space', 'spare', 'spatial', 'spawn', 'speak', 'special', 'speed', 'spell', 'spend', 'sphere', 'spice', 'spider', 'spike', 'spin', 'spirit', 'split', 'spoil', 'sponsor', 'spoon', 'sport', 'spot', 'spray', 'spread', 'spring', 'spy', 'square', 'squeeze', 'squirrel', 'stable', 'stadium', 'staff', 'stage', 'stairs', 'stamp', 'stand', 'start', 'state', 'stay', 'steak', 'steel', 'stem', 'step', 'stereo', 'stick', 'still', 'sting', 'stock', 'stomach', 'stone', 'stool', 'story', 'stove', 'strategy', 'street', 'strike', 'strong', 'struggle', 'student', 'stuff', 'stumble', 'style', 'subject', 'submit', 'subway', 'success', 'such', 'sudden', 'suffer', 'sugar', 'suggest', 'suit', 'summer', 'sun', 'sunny', 'sunset', 'super', 'supply', 'supreme', 'sure', 'surface', 'surge', 'surprise', 'surround', 'survey', 'suspect', 'sustain', 'swallow', 'swamp', 'swap', 'swarm', 'swear', 'sweet', 'swift', 'swim', 'swing', 'switch', 'sword', 'symbol', 'symptom', 'syrup', 'system', 'table', 'tackle', 'tag', 'tail', 'talent', 'talk', 'tank', 'tape', 'target', 'task', 'taste', 'tattoo', 'taxi', 'teach', 'team', 'tell', 'ten', 'tenant', 'tennis', 'tent', 'term', 'test', 'text', 'thank', 'that', 'theme', 'then', 'theory', 'there', 'they', 'thing', 'this', 'thought', 'three', 'thrive', 'throw', 'thumb', 'thunder', 'ticket', 'tide', 'tiger', 'tilt', 'timber', 'time', 'tiny', 'tip', 'tired', 'tissue', 'title', 'toast', 'tobacco', 'today', 'toddler', 'toe', 'together', 'toilet', 'token', 'tomato', 'tomorrow', 'tone', 'tongue', 'tonight', 'tool', 'tooth', 'top', 'topic', 'topple', 'torch', 'tornado', 'tortoise', 'toss', 'total', 'tourist', 'toward', 'tower', 'town', 'toy', 'track', 'trade', 'traffic', 'tragic', 'train', 'transfer', 'trap', 'trash', 'travel', 'tray', 'treat', 'tree', 'trend', 'trial', 'tribe', 'trick', 'trigger', 'trim', 'trip', 'trophy', 'trouble', 'truck', 'true', 'truly', 'trumpet', 'trust', 'truth', 'try', 'tube', 'tuition', 'tumble', 'tuna', 'tunnel', 'turkey', 'turn', 'turtle', 'twelve', 'twenty', 'twice', 'twin', 'twist', 'two', 'type', 'typical', 'ugly', 'umbrella', 'unable', 'unaware', 'uncle', 'uncover', 'under', 'undo', 'unfair', 'unfold', 'unhappy', 'uniform', 'unique', 'unit', 'universe', 'unknown', 'unlock', 'until', 'unusual', 'unveil', 'update', 'upgrade', 'uphold', 'upon', 'upper', 'upset', 'urban', 'urge', 'usage', 'use', 'used', 'useful', 'useless', 'usual', 'utility', 'vacant', 'vacuum', 'vague', 'valid', 'valley', 'valve', 'van', 'vanish', 'vapor', 'various', 'vast', 'vault', 'vehicle', 'velvet', 'vendor', 'venture', 'venue', 'verb', 'verify', 'version', 'very', 'vessel', 'veteran', 'viable', 'vibrant', 'vicious', 'victory', 'video', 'view', 'village', 'vintage', 'violin', 'virtual', 'virus', 'visa', 'visit', 'visual', 'vital', 'vivid', 'vocal', 'voice', 'void', 'volcano', 'volume', 'vote', 'voyage', 'wage', 'wagon', 'wait', 'walk', 'wall', 'walnut', 'want', 'warfare', 'warm', 'warrior', 'wash', 'wasp', 'waste', 'water', 'wave', 'way', 'wealth', 'weapon', 'wear', 'weasel', 'weather', 'web', 'wedding', 'weekend', 'weird', 'welcome', 'west', 'wet', 'whale', 'what', 'wheat', 'wheel', 'when', 'where', 'whip', 'whisper', 'wide', 'width', 'wife', 'wild', 'will', 'win', 'window', 'wine', 'wing', 'wink', 'winner', 'winter', 'wire', 'wisdom', 'wise', 'wish', 'witness', 'wolf', 'woman', 'wonder', 'wood', 'wool', 'word', 'work', 'world', 'worry', 'worth', 'wrap', 'wreck', 'wrestle', 'wrist', 'write', 'wrong', 'yard', 'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone', 'zoo']) +def mnemonic_to_key(mnemonic, wordlist=['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual', 'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance', 'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent', 'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album', 'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone', 'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among', 'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry', 'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique', 'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april', 'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor', 'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact', 'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume', 'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction', 'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado', 'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis', 'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball', 'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base', 'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become', 'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt', 'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle', 'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black', 'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood', 'blossom', 'blouse', 'blue', 'blur', 'blush', 'board', 'boat', 'body', 'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring', 'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain', 'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief', 'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother', 'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb', 'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus', 'business', 'busy', 'butter', 'buyer', 'buzz', 'cabbage', 'cabin', 'cable', 'cactus', 'cage', 'cake', 'call', 'calm', 'camera', 'camp', 'can', 'canal', 'cancel', 'candy', 'cannon', 'canoe', 'canvas', 'canyon', 'capable', 'capital', 'captain', 'car', 'carbon', 'card', 'cargo', 'carpet', 'carry', 'cart', 'case', 'cash', 'casino', 'castle', 'casual', 'cat', 'catalog', 'catch', 'category', 'cattle', 'caught', 'cause', 'caution', 'cave', 'ceiling', 'celery', 'cement', 'census', 'century', 'cereal', 'certain', 'chair', 'chalk', 'champion', 'change', 'chaos', 'chapter', 'charge', 'chase', 'chat', 'cheap', 'check', 'cheese', 'chef', 'cherry', 'chest', 'chicken', 'chief', 'child', 'chimney', 'choice', 'choose', 'chronic', 'chuckle', 'chunk', 'churn', 'cigar', 'cinnamon', 'circle', 'citizen', 'city', 'civil', 'claim', 'clap', 'clarify', 'claw', 'clay', 'clean', 'clerk', 'clever', 'click', 'client', 'cliff', 'climb', 'clinic', 'clip', 'clock', 'clog', 'close', 'cloth', 'cloud', 'clown', 'club', 'clump', 'cluster', 'clutch', 'coach', 'coast', 'coconut', 'code', 'coffee', 'coil', 'coin', 'collect', 'color', 'column', 'combine', 'come', 'comfort', 'comic', 'common', 'company', 'concert', 'conduct', 'confirm', 'congress', 'connect', 'consider', 'control', 'convince', 'cook', 'cool', 'copper', 'copy', 'coral', 'core', 'corn', 'correct', 'cost', 'cotton', 'couch', 'country', 'couple', 'course', 'cousin', 'cover', 'coyote', 'crack', 'cradle', 'craft', 'cram', 'crane', 'crash', 'crater', 'crawl', 'crazy', 'cream', 'credit', 'creek', 'crew', 'cricket', 'crime', 'crisp', 'critic', 'crop', 'cross', 'crouch', 'crowd', 'crucial', 'cruel', 'cruise', 'crumble', 'crunch', 'crush', 'cry', 'crystal', 'cube', 'culture', 'cup', 'cupboard', 'curious', 'current', 'curtain', 'curve', 'cushion', 'custom', 'cute', 'cycle', 'dad', 'damage', 'damp', 'dance', 'danger', 'daring', 'dash', 'daughter', 'dawn', 'day', 'deal', 'debate', 'debris', 'decade', 'december', 'decide', 'decline', 'decorate', 'decrease', 'deer', 'defense', 'define', 'defy', 'degree', 'delay', 'deliver', 'demand', 'demise', 'denial', 'dentist', 'deny', 'depart', 'depend', 'deposit', 'depth', 'deputy', 'derive', 'describe', 'desert', 'design', 'desk', 'despair', 'destroy', 'detail', 'detect', 'develop', 'device', 'devote', 'diagram', 'dial', 'diamond', 'diary', 'dice', 'diesel', 'diet', 'differ', 'digital', 'dignity', 'dilemma', 'dinner', 'dinosaur', 'direct', 'dirt', 'disagree', 'discover', 'disease', 'dish', 'dismiss', 'disorder', 'display', 'distance', 'divert', 'divide', 'divorce', 'dizzy', 'doctor', 'document', 'dog', 'doll', 'dolphin', 'domain', 'donate', 'donkey', 'donor', 'door', 'dose', 'double', 'dove', 'draft', 'dragon', 'drama', 'drastic', 'draw', 'dream', 'dress', 'drift', 'drill', 'drink', 'drip', 'drive', 'drop', 'drum', 'dry', 'duck', 'dumb', 'dune', 'during', 'dust', 'dutch', 'duty', 'dwarf', 'dynamic', 'eager', 'eagle', 'early', 'earn', 'earth', 'easily', 'east', 'easy', 'echo', 'ecology', 'economy', 'edge', 'edit', 'educate', 'effort', 'egg', 'eight', 'either', 'elbow', 'elder', 'electric', 'elegant', 'element', 'elephant', 'elevator', 'elite', 'else', 'embark', 'embody', 'embrace', 'emerge', 'emotion', 'employ', 'empower', 'empty', 'enable', 'enact', 'end', 'endless', 'endorse', 'enemy', 'energy', 'enforce', 'engage', 'engine', 'enhance', 'enjoy', 'enlist', 'enough', 'enrich', 'enroll', 'ensure', 'enter', 'entire', 'entry', 'envelope', 'episode', 'equal', 'equip', 'era', 'erase', 'erode', 'erosion', 'error', 'erupt', 'escape', 'essay', 'essence', 'estate', 'eternal', 'ethics', 'evidence', 'evil', 'evoke', 'evolve', 'exact', 'example', 'excess', 'exchange', 'excite', 'exclude', 'excuse', 'execute', 'exercise', 'exhaust', 'exhibit', 'exile', 'exist', 'exit', 'exotic', 'expand', 'expect', 'expire', 'explain', 'expose', 'express', 'extend', 'extra', 'eye', 'eyebrow', 'fabric', 'face', 'faculty', 'fade', 'faint', 'faith', 'fall', 'false', 'fame', 'family', 'famous', 'fan', 'fancy', 'fantasy', 'farm', 'fashion', 'fat', 'fatal', 'father', 'fatigue', 'fault', 'favorite', 'feature', 'february', 'federal', 'fee', 'feed', 'feel', 'female', 'fence', 'festival', 'fetch', 'fever', 'few', 'fiber', 'fiction', 'field', 'figure', 'file', 'film', 'filter', 'final', 'find', 'fine', 'finger', 'finish', 'fire', 'firm', 'first', 'fiscal', 'fish', 'fit', 'fitness', 'fix', 'flag', 'flame', 'flash', 'flat', 'flavor', 'flee', 'flight', 'flip', 'float', 'flock', 'floor', 'flower', 'fluid', 'flush', 'fly', 'foam', 'focus', 'fog', 'foil', 'fold', 'follow', 'food', 'foot', 'force', 'forest', 'forget', 'fork', 'fortune', 'forum', 'forward', 'fossil', 'foster', 'found', 'fox', 'fragile', 'frame', 'frequent', 'fresh', 'friend', 'fringe', 'frog', 'front', 'frost', 'frown', 'frozen', 'fruit', 'fuel', 'fun', 'funny', 'furnace', 'fury', 'future', 'gadget', 'gain', 'galaxy', 'gallery', 'game', 'gap', 'garage', 'garbage', 'garden', 'garlic', 'garment', 'gas', 'gasp', 'gate', 'gather', 'gauge', 'gaze', 'general', 'genius', 'genre', 'gentle', 'genuine', 'gesture', 'ghost', 'giant', 'gift', 'giggle', 'ginger', 'giraffe', 'girl', 'give', 'glad', 'glance', 'glare', 'glass', 'glide', 'glimpse', 'globe', 'gloom', 'glory', 'glove', 'glow', 'glue', 'goat', 'goddess', 'gold', 'good', 'goose', 'gorilla', 'gospel', 'gossip', 'govern', 'gown', 'grab', 'grace', 'grain', 'grant', 'grape', 'grass', 'gravity', 'great', 'green', 'grid', 'grief', 'grit', 'grocery', 'group', 'grow', 'grunt', 'guard', 'guess', 'guide', 'guilt', 'guitar', 'gun', 'gym', 'habit', 'hair', 'half', 'hammer', 'hamster', 'hand', 'happy', 'harbor', 'hard', 'harsh', 'harvest', 'hat', 'have', 'hawk', 'hazard', 'head', 'health', 'heart', 'heavy', 'hedgehog', 'height', 'hello', 'helmet', 'help', 'hen', 'hero', 'hidden', 'high', 'hill', 'hint', 'hip', 'hire', 'history', 'hobby', 'hockey', 'hold', 'hole', 'holiday', 'hollow', 'home', 'honey', 'hood', 'hope', 'horn', 'horror', 'horse', 'hospital', 'host', 'hotel', 'hour', 'hover', 'hub', 'huge', 'human', 'humble', 'humor', 'hundred', 'hungry', 'hunt', 'hurdle', 'hurry', 'hurt', 'husband', 'hybrid', 'ice', 'icon', 'idea', 'identify', 'idle', 'ignore', 'ill', 'illegal', 'illness', 'image', 'imitate', 'immense', 'immune', 'impact', 'impose', 'improve', 'impulse', 'inch', 'include', 'income', 'increase', 'index', 'indicate', 'indoor', 'industry', 'infant', 'inflict', 'inform', 'inhale', 'inherit', 'initial', 'inject', 'injury', 'inmate', 'inner', 'innocent', 'input', 'inquiry', 'insane', 'insect', 'inside', 'inspire', 'install', 'intact', 'interest', 'into', 'invest', 'invite', 'involve', 'iron', 'island', 'isolate', 'issue', 'item', 'ivory', 'jacket', 'jaguar', 'jar', 'jazz', 'jealous', 'jeans', 'jelly', 'jewel', 'job', 'join', 'joke', 'journey', 'joy', 'judge', 'juice', 'jump', 'jungle', 'junior', 'junk', 'just', 'kangaroo', 'keen', 'keep', 'ketchup', 'key', 'kick', 'kid', 'kidney', 'kind', 'kingdom', 'kiss', 'kit', 'kitchen', 'kite', 'kitten', 'kiwi', 'knee', 'knife', 'knock', 'know', 'lab', 'label', 'labor', 'ladder', 'lady', 'lake', 'lamp', 'language', 'laptop', 'large', 'later', 'latin', 'laugh', 'laundry', 'lava', 'law', 'lawn', 'lawsuit', 'layer', 'lazy', 'leader', 'leaf', 'learn', 'leave', 'lecture', 'left', 'leg', 'legal', 'legend', 'leisure', 'lemon', 'lend', 'length', 'lens', 'leopard', 'lesson', 'letter', 'level', 'liar', 'liberty', 'library', 'license', 'life', 'lift', 'light', 'like', 'limb', 'limit', 'link', 'lion', 'liquid', 'list', 'little', 'live', 'lizard', 'load', 'loan', 'lobster', 'local', 'lock', 'logic', 'lonely', 'long', 'loop', 'lottery', 'loud', 'lounge', 'love', 'loyal', 'lucky', 'luggage', 'lumber', 'lunar', 'lunch', 'luxury', 'lyrics', 'machine', 'mad', 'magic', 'magnet', 'maid', 'mail', 'main', 'major', 'make', 'mammal', 'man', 'manage', 'mandate', 'mango', 'mansion', 'manual', 'maple', 'marble', 'march', 'margin', 'marine', 'market', 'marriage', 'mask', 'mass', 'master', 'match', 'material', 'math', 'matrix', 'matter', 'maximum', 'maze', 'meadow', 'mean', 'measure', 'meat', 'mechanic', 'medal', 'media', 'melody', 'melt', 'member', 'memory', 'mention', 'menu', 'mercy', 'merge', 'merit', 'merry', 'mesh', 'message', 'metal', 'method', 'middle', 'midnight', 'milk', 'million', 'mimic', 'mind', 'minimum', 'minor', 'minute', 'miracle', 'mirror', 'misery', 'miss', 'mistake', 'mix', 'mixed', 'mixture', 'mobile', 'model', 'modify', 'mom', 'moment', 'monitor', 'monkey', 'monster', 'month', 'moon', 'moral', 'more', 'morning', 'mosquito', 'mother', 'motion', 'motor', 'mountain', 'mouse', 'move', 'movie', 'much', 'muffin', 'mule', 'multiply', 'muscle', 'museum', 'mushroom', 'music', 'must', 'mutual', 'myself', 'mystery', 'myth', 'naive', 'name', 'napkin', 'narrow', 'nasty', 'nation', 'nature', 'near', 'neck', 'need', 'negative', 'neglect', 'neither', 'nephew', 'nerve', 'nest', 'net', 'network', 'neutral', 'never', 'news', 'next', 'nice', 'night', 'noble', 'noise', 'nominee', 'noodle', 'normal', 'north', 'nose', 'notable', 'note', 'nothing', 'notice', 'novel', 'now', 'nuclear', 'number', 'nurse', 'nut', 'oak', 'obey', 'object', 'oblige', 'obscure', 'observe', 'obtain', 'obvious', 'occur', 'ocean', 'october', 'odor', 'off', 'offer', 'office', 'often', 'oil', 'okay', 'old', 'olive', 'olympic', 'omit', 'once', 'one', 'onion', 'online', 'only', 'open', 'opera', 'opinion', 'oppose', 'option', 'orange', 'orbit', 'orchard', 'order', 'ordinary', 'organ', 'orient', 'original', 'orphan', 'ostrich', 'other', 'outdoor', 'outer', 'output', 'outside', 'oval', 'oven', 'over', 'own', 'owner', 'oxygen', 'oyster', 'ozone', 'pact', 'paddle', 'page', 'pair', 'palace', 'palm', 'panda', 'panel', 'panic', 'panther', 'paper', 'parade', 'parent', 'park', 'parrot', 'party', 'pass', 'patch', 'path', 'patient', 'patrol', 'pattern', 'pause', 'pave', 'payment', 'peace', 'peanut', 'pear', 'peasant', 'pelican', 'pen', 'penalty', 'pencil', 'people', 'pepper', 'perfect', 'permit', 'person', 'pet', 'phone', 'photo', 'phrase', 'physical', 'piano', 'picnic', 'picture', 'piece', 'pig', 'pigeon', 'pill', 'pilot', 'pink', 'pioneer', 'pipe', 'pistol', 'pitch', 'pizza', 'place', 'planet', 'plastic', 'plate', 'play', 'please', 'pledge', 'pluck', 'plug', 'plunge', 'poem', 'poet', 'point', 'polar', 'pole', 'police', 'pond', 'pony', 'pool', 'popular', 'portion', 'position', 'possible', 'post', 'potato', 'pottery', 'poverty', 'powder', 'power', 'practice', 'praise', 'predict', 'prefer', 'prepare', 'present', 'pretty', 'prevent', 'price', 'pride', 'primary', 'print', 'priority', 'prison', 'private', 'prize', 'problem', 'process', 'produce', 'profit', 'program', 'project', 'promote', 'proof', 'property', 'prosper', 'protect', 'proud', 'provide', 'public', 'pudding', 'pull', 'pulp', 'pulse', 'pumpkin', 'punch', 'pupil', 'puppy', 'purchase', 'purity', 'purpose', 'purse', 'push', 'put', 'puzzle', 'pyramid', 'quality', 'quantum', 'quarter', 'question', 'quick', 'quit', 'quiz', 'quote', 'rabbit', 'raccoon', 'race', 'rack', 'radar', 'radio', 'rail', 'rain', 'raise', 'rally', 'ramp', 'ranch', 'random', 'range', 'rapid', 'rare', 'rate', 'rather', 'raven', 'raw', 'razor', 'ready', 'real', 'reason', 'rebel', 'rebuild', 'recall', 'receive', 'recipe', 'record', 'recycle', 'reduce', 'reflect', 'reform', 'refuse', 'region', 'regret', 'regular', 'reject', 'relax', 'release', 'relief', 'rely', 'remain', 'remember', 'remind', 'remove', 'render', 'renew', 'rent', 'reopen', 'repair', 'repeat', 'replace', 'report', 'require', 'rescue', 'resemble', 'resist', 'resource', 'response', 'result', 'retire', 'retreat', 'return', 'reunion', 'reveal', 'review', 'reward', 'rhythm', 'rib', 'ribbon', 'rice', 'rich', 'ride', 'ridge', 'rifle', 'right', 'rigid', 'ring', 'riot', 'ripple', 'risk', 'ritual', 'rival', 'river', 'road', 'roast', 'robot', 'robust', 'rocket', 'romance', 'roof', 'rookie', 'room', 'rose', 'rotate', 'rough', 'round', 'route', 'royal', 'rubber', 'rude', 'rug', 'rule', 'run', 'runway', 'rural', 'sad', 'saddle', 'sadness', 'safe', 'sail', 'salad', 'salmon', 'salon', 'salt', 'salute', 'same', 'sample', 'sand', 'satisfy', 'satoshi', 'sauce', 'sausage', 'save', 'say', 'scale', 'scan', 'scare', 'scatter', 'scene', 'scheme', 'school', 'science', 'scissors', 'scorpion', 'scout', 'scrap', 'screen', 'script', 'scrub', 'sea', 'search', 'season', 'seat', 'second', 'secret', 'section', 'security', 'seed', 'seek', 'segment', 'select', 'sell', 'seminar', 'senior', 'sense', 'sentence', 'series', 'service', 'session', 'settle', 'setup', 'seven', 'shadow', 'shaft', 'shallow', 'share', 'shed', 'shell', 'sheriff', 'shield', 'shift', 'shine', 'ship', 'shiver', 'shock', 'shoe', 'shoot', 'shop', 'short', 'shoulder', 'shove', 'shrimp', 'shrug', 'shuffle', 'shy', 'sibling', 'sick', 'side', 'siege', 'sight', 'sign', 'silent', 'silk', 'silly', 'silver', 'similar', 'simple', 'since', 'sing', 'siren', 'sister', 'situate', 'six', 'size', 'skate', 'sketch', 'ski', 'skill', 'skin', 'skirt', 'skull', 'slab', 'slam', 'sleep', 'slender', 'slice', 'slide', 'slight', 'slim', 'slogan', 'slot', 'slow', 'slush', 'small', 'smart', 'smile', 'smoke', 'smooth', 'snack', 'snake', 'snap', 'sniff', 'snow', 'soap', 'soccer', 'social', 'sock', 'soda', 'soft', 'solar', 'soldier', 'solid', 'solution', 'solve', 'someone', 'song', 'soon', 'sorry', 'sort', 'soul', 'sound', 'soup', 'source', 'south', 'space', 'spare', 'spatial', 'spawn', 'speak', 'special', 'speed', 'spell', 'spend', 'sphere', 'spice', 'spider', 'spike', 'spin', 'spirit', 'split', 'spoil', 'sponsor', 'spoon', 'sport', 'spot', 'spray', 'spread', 'spring', 'spy', 'square', 'squeeze', 'squirrel', 'stable', 'stadium', 'staff', 'stage', 'stairs', 'stamp', 'stand', 'start', 'state', 'stay', 'steak', 'steel', 'stem', 'step', 'stereo', 'stick', 'still', 'sting', 'stock', 'stomach', 'stone', 'stool', 'story', 'stove', 'strategy', 'street', 'strike', 'strong', 'struggle', 'student', 'stuff', 'stumble', 'style', 'subject', 'submit', 'subway', 'success', 'such', 'sudden', 'suffer', 'sugar', 'suggest', 'suit', 'summer', 'sun', 'sunny', 'sunset', 'super', 'supply', 'supreme', 'sure', 'surface', 'surge', 'surprise', 'surround', 'survey', 'suspect', 'sustain', 'swallow', 'swamp', 'swap', 'swarm', 'swear', 'sweet', 'swift', 'swim', 'swing', 'switch', 'sword', 'symbol', 'symptom', 'syrup', 'system', 'table', 'tackle', 'tag', 'tail', 'talent', 'talk', 'tank', 'tape', 'target', 'task', 'taste', 'tattoo', 'taxi', 'teach', 'team', 'tell', 'ten', 'tenant', 'tennis', 'tent', 'term', 'test', 'text', 'thank', 'that', 'theme', 'then', 'theory', 'there', 'they', 'thing', 'this', 'thought', 'three', 'thrive', 'throw', 'thumb', 'thunder', 'ticket', 'tide', 'tiger', 'tilt', 'timber', 'time', 'tiny', 'tip', 'tired', 'tissue', 'title', 'toast', 'tobacco', 'today', 'toddler', 'toe', 'together', 'toilet', 'token', 'tomato', 'tomorrow', 'tone', 'tongue', 'tonight', 'tool', 'tooth', 'top', 'topic', 'topple', 'torch', 'tornado', 'tortoise', 'toss', 'total', 'tourist', 'toward', 'tower', 'town', 'toy', 'track', 'trade', 'traffic', 'tragic', 'train', 'transfer', 'trap', 'trash', 'travel', 'tray', 'treat', 'tree', 'trend', 'trial', 'tribe', 'trick', 'trigger', 'trim', 'trip', 'trophy', 'trouble', 'truck', 'true', 'truly', 'trumpet', 'trust', 'truth', 'try', 'tube', 'tuition', 'tumble', 'tuna', 'tunnel', 'turkey', 'turn', 'turtle', 'twelve', 'twenty', 'twice', 'twin', 'twist', 'two', 'type', 'typical', 'ugly', 'umbrella', 'unable', 'unaware', 'uncle', 'uncover', 'under', 'undo', 'unfair', 'unfold', 'unhappy', 'uniform', 'unique', 'unit', 'universe', 'unknown', 'unlock', 'until', 'unusual', 'unveil', 'update', 'upgrade', 'uphold', 'upon', 'upper', 'upset', 'urban', 'urge', 'usage', 'use', 'used', 'useful', 'useless', 'usual', 'utility', 'vacant', 'vacuum', 'vague', 'valid', 'valley', 'valve', 'van', 'vanish', 'vapor', 'various', 'vast', 'vault', 'vehicle', 'velvet', 'vendor', 'venture', 'venue', 'verb', 'verify', 'version', 'very', 'vessel', 'veteran', 'viable', 'vibrant', 'vicious', 'victory', 'video', 'view', 'village', 'vintage', 'violin', 'virtual', 'virus', 'visa', 'visit', 'visual', 'vital', 'vivid', 'vocal', 'voice', 'void', 'volcano', 'volume', 'vote', 'voyage', 'wage', 'wagon', 'wait', 'walk', 'wall', 'walnut', 'want', 'warfare', 'warm', 'warrior', 'wash', 'wasp', 'waste', 'water', 'wave', 'way', 'wealth', 'weapon', 'wear', 'weasel', 'weather', 'web', 'wedding', 'weekend', 'weird', 'welcome', 'west', 'wet', 'whale', 'what', 'wheat', 'wheel', 'when', 'where', 'whip', 'whisper', 'wide', 'width', 'wife', 'wild', 'will', 'win', 'window', 'wine', 'wing', 'wink', 'winner', 'winter', 'wire', 'wisdom', 'wise', 'wish', 'witness', 'wolf', 'woman', 'wonder', 'wood', 'wool', 'word', 'work', 'world', 'worry', 'worth', 'wrap', 'wreck', 'wrestle', 'wrist', 'write', 'wrong', 'yard', 'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone', 'zoo'])
-

Convert mnemonic to key

+

Convert mnemonic to key

Args

mnemonic : str
A string of space separated words representing the mnemonic.
-
wordlist : list[string], optional
+
wordlist : list[string], optional
The Word list used to generate the mnemonic. Defaults to wordlist.

Raises

-
FailedChecksumError
+
FailedChecksumError
Thrown when the checksum validation fails.

Returns

-
bytes
+
bytes
The key
-
+
-Source code + +Expand source code +
def mnemonic_to_key(mnemonic, wordlist=wordlist):
     """Convert mnemonic to key
 
@@ -346,7 +360,7 @@ 

Returns

def to_bin(arr, bytelen=8)
-

Converts an iterable object containing ascii codes to a string of the joint binary representation of each item where each of them is of length bytelen

+

Converts an iterable object containing ascii codes to a string of the joint binary representation of each item where each of them is of length bytelen

Args

arr : iterable
@@ -356,11 +370,13 @@

Args

Returns

-
string
+
string
The binary string representing the array
-
+
-Source code + +Expand source code +
def to_bin(arr, bytelen=8):
     """Converts an iterable object containing ascii codes to a string of the joint binary representation of each item where each of them is of length bytelen
 
@@ -407,9 +423,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/encryption/wordlist.html b/docs/api/jumpscale/data/encryption/wordlist.html index 75105da84..bd637df3a 100644 --- a/docs/api/jumpscale/data/encryption/wordlist.html +++ b/docs/api/jumpscale/data/encryption/wordlist.html @@ -3,15 +3,17 @@ - + jumpscale.data.encryption.wordlist API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.encryption.wordlist

-Source code + +Expand source code +
WORDS = [
     "abandon",
     "ability",
@@ -2098,9 +2102,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/fake/index.html b/docs/api/jumpscale/data/fake/index.html index 6ee3a65e0..821b48ab9 100644 --- a/docs/api/jumpscale/data/fake/index.html +++ b/docs/api/jumpscale/data/fake/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.fake API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.fake

-Source code + +Expand source code +
def export_module_as():
     from faker import Faker
     import sys
@@ -40,9 +44,11 @@ 

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
     from faker import Faker
     import sys
@@ -75,9 +81,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/hash/hash.html b/docs/api/jumpscale/data/hash/hash.html index 8b06cf5df..70471db8a 100644 --- a/docs/api/jumpscale/data/hash/hash.html +++ b/docs/api/jumpscale/data/hash/hash.html @@ -3,15 +3,17 @@ - + jumpscale.data.hash.hash API documentation - - - - - + + + + + + +
@@ -21,47 +23,49 @@

Module jumpscale.data.hash.hash

This module helps with everything related to hashing strings, bytes with popular algorithms like, md5, sha256, sha384, sha512, blake2

-
JS-NG> j.data.hash.md5("abc")                                                                       
+
JS-NG> j.data.hash.md5("abc")
 '900150983cd24fb0d6963f7d28e17f72'
 
-JS-NG> j.data.hash.sha1("abc")                                                                      
+JS-NG> j.data.hash.sha1("abc")
 'a9993e364706816aba3e25717850c26c9cd0d89d'
 
-JS-NG> j.data.hash.sha224("abc")                                                                    
+JS-NG> j.data.hash.sha224("abc")
 '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'
 
-JS-NG> j.data.hash.sha512("abc")                                                                    
+JS-NG> j.data.hash.sha512("abc")
 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454
 d4423643ce80e2a9ac94fa54ca49f'
 
-JS-NG> j.data.hash.blake2s("abc")                                                                   
+JS-NG> j.data.hash.blake2s("abc")
 '508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982'
 
 JS-NG>
 
-Source code + +Expand source code +
"""This module helps with everything related to hashing strings, bytes with popular algorithms like, md5, sha256, sha384, sha512, blake2
 
 ```
-JS-NG> j.data.hash.md5("abc")                                                                       
+JS-NG> j.data.hash.md5("abc")
 '900150983cd24fb0d6963f7d28e17f72'
 
-JS-NG> j.data.hash.sha1("abc")                                                                      
+JS-NG> j.data.hash.sha1("abc")
 'a9993e364706816aba3e25717850c26c9cd0d89d'
 
-JS-NG> j.data.hash.sha224("abc")                                                                    
+JS-NG> j.data.hash.sha224("abc")
 '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'
 
-JS-NG> j.data.hash.sha512("abc")                                                                    
+JS-NG> j.data.hash.sha512("abc")
 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454
 d4423643ce80e2a9ac94fa54ca49f'
 
-JS-NG> j.data.hash.blake2s("abc")                                                                   
+JS-NG> j.data.hash.blake2s("abc")
 '508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982'
 
 JS-NG>
-```  
+```
 """
 
 import hashlib
@@ -369,24 +373,23 @@ 

Module jumpscale.data.hash.hash

Functions

-def blake2b(string, encode='utf-8') +def blake2b(string, encode='utf-8')
-

create a blake2b hash string for any string

+

create a blake2b hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def blake2b(string, encode="utf-8"):
     """create a blake2b hash string for any string
 
@@ -403,24 +406,23 @@ 

Returns

-def blake2s(string, encode='utf-8') +def blake2s(string, encode='utf-8')
-

create a blake2s hash string for any string

+

create a blake2s hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def blake2s(string, encode="utf-8"):
     """create a blake2s hash string for any string
 
@@ -440,9 +442,11 @@ 

Returns

def encode_string(obj, encode)
-
+
-Source code + +Expand source code +
def encode_string(obj, encode):
     if isinstance(obj, str):
         return obj.encode(encode)
@@ -453,16 +457,15 @@ 

Returns

def get_list_files(dir_name)
-

returns a list of directories for all files in a root folder

+

returns a list of directories for all files in a root folder

Arguments

dir_name (str) : the directory of the root folder

Returns

-
-
all_files (list) : the list of directories for all files in the root folder
-
 
-
+

all_files (list) : the list of directories for all files in the root folder

-Source code + +Expand source code +
def get_list_files(dir_name):
     """returns a list of directories for all files in a root folder
 
@@ -493,17 +496,19 @@ 

Returns

def hash_alg(name, data, **kwargs)
-

create a hash object

+

create a hash object

Arguments

name (str) : name of algorithm name data (str) : data need to be hashed

Returns

-
list of byte : the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def hash_alg(name, data, **kwargs):
     """create a hash object
 
@@ -521,17 +526,19 @@ 

Returns

def hash_directory(root_dir, hash_type)
-

create hash string list for the files in a folder

+

create hash string list for the files in a folder

Arguments

root_dir (str) : the dir for the root folder hash_type (str) : the type of the hash

Returns

-
dict : the hashes dict, keys are full paths and values are hexdigests
-
 
-
+
dict
+
the hashes dict, keys are full paths and values are hexdigests
+
-Source code + +Expand source code +
def hash_directory(root_dir, hash_type):
     """create hash string list for the files in a folder
 
@@ -552,17 +559,19 @@ 

Returns

def hash_file(path, hash_type)
-

create hash string for a file

+

create hash string for a file

Arguments

path (str) : the path for the file hash_type (str) : the type of the hash

Returns

-
list of byte : the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def hash_file(path, hash_type):
     """create hash string for a file
 
@@ -584,24 +593,23 @@ 

Returns

-def md5(string, encode='utf-8') +def md5(string, encode='utf-8')
-

create a md5 hash string for any string

+

create a md5 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def md5(string, encode="utf-8"):
     """create a md5 hash string for any string
 
@@ -618,24 +626,23 @@ 

Returns

-def sha1(string, encode='utf-8') +def sha1(string, encode='utf-8')
-

create a sha1 hash string for any string

+

create a sha1 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha1(string, encode="utf-8"):
     """create a sha1 hash string for any string
 
@@ -652,24 +659,23 @@ 

Returns

-def sha224(string, encode='utf-8') +def sha224(string, encode='utf-8')
-

create a sha224 hash string for any string

+

create a sha224 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha224(string, encode="utf-8"):
     """create a sha224 hash string for any string
 
@@ -686,24 +692,23 @@ 

Returns

-def sha256(string, encode='utf-8') +def sha256(string, encode='utf-8')
-

create a sha256 hash string for any string

+

create a sha256 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha256(string, encode="utf-8"):
     """create a sha256 hash string for any string
 
@@ -720,24 +725,23 @@ 

Returns

-def sha384(string, encode='utf-8') +def sha384(string, encode='utf-8')
-

create a sha384 hash string for any string

+

create a sha384 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha384(string, encode="utf-8"):
     """create a sha384 hash string for any string
 
@@ -754,24 +758,23 @@ 

Returns

-def sha3_224(string, encode='utf-8') +def sha3_224(string, encode='utf-8')
-

create a sha3_224 hash string for any string

+

create a sha3_224 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha3_224(string, encode="utf-8"):
     """create a sha3_224 hash string for any string
 
@@ -788,24 +791,23 @@ 

Returns

-def sha3_256(string, encode='utf-8') +def sha3_256(string, encode='utf-8')
-

create a sha3_256 hash string for any string

+

create a sha3_256 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha3_256(string, encode="utf-8"):
     """create a sha3_256 hash string for any string
 
@@ -822,24 +824,23 @@ 

Returns

-def sha3_384(string, encode='utf-8') +def sha3_384(string, encode='utf-8')
-

create a sha3_384 hash string for any string

+

create a sha3_384 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha3_384(string, encode="utf-8"):
     """create a sha3_384 hash string for any string
 
@@ -856,24 +857,23 @@ 

Returns

-def sha3_512(string, encode='utf-8') +def sha3_512(string, encode='utf-8')
-

create a sha3_512 hash string for any string

+

create a sha3_512 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha3_512(string, encode="utf-8"):
     """create a sha3_512 hash string for any string
 
@@ -890,24 +890,23 @@ 

Returns

-def sha512(string, encode='utf-8') +def sha512(string, encode='utf-8')
-

create a sha512 hash string for any string

+

create a sha512 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def sha512(string, encode="utf-8"):
     """create a sha512 hash string for any string
 
@@ -924,24 +923,23 @@ 

Returns

-def shake_128(string, encode='utf-8') +def shake_128(string, encode='utf-8')
-

create a shake_128 hash string for any string

+

create a shake_128 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def shake_128(string, encode="utf-8"):
     """create a shake_128 hash string for any string
 
@@ -958,24 +956,23 @@ 

Returns

-def shake_256(string, encode='utf-8') +def shake_256(string, encode='utf-8')
-

create a shake_256 hash string for any string

+

create a shake_256 hash string for any string

Arguments

-
-
string : str
-
the string need to be hashed
-
+

string (str): the string need to be hashed

Keyword Arguments: encode (str): the encoding for the string (default: {"utf-8"})

Returns

-
list of byte: the hash bytes
-
 
-
+
list of byte
+
the hash bytes
+
-Source code + +Expand source code +
def shake_256(string, encode="utf-8"):
     """create a shake_256 hash string for any string
 
@@ -1034,9 +1031,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/hash/index.html b/docs/api/jumpscale/data/hash/index.html index b7cdac903..4f09762a4 100644 --- a/docs/api/jumpscale/data/hash/index.html +++ b/docs/api/jumpscale/data/hash/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.hash API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.hash

-Source code + +Expand source code +
from .hash import *
@@ -30,7 +34,7 @@

Sub-modules

jumpscale.data.hash.hash
-

This module helps with everything related to hashing strings, bytes with popular algorithms like, md5, sha256, sha384, sha512, blake2 …

+

This module helps with everything related to hashing strings, bytes with popular algorithms like, md5, sha256, sha384, sha512, blake2 …

@@ -61,9 +65,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/idgenerator/idgenerator.html b/docs/api/jumpscale/data/idgenerator/idgenerator.html index 271354d4a..e8bd397c4 100644 --- a/docs/api/jumpscale/data/idgenerator/idgenerator.html +++ b/docs/api/jumpscale/data/idgenerator/idgenerator.html @@ -3,15 +3,17 @@ - + jumpscale.data.idgenerator.idgenerator API documentation - - - - - + + + + + + +
@@ -21,39 +23,41 @@

Module jumpscale.data.idgenerator.idgenerator

idgenerator module helps with generating ids, guids, integers, chars, passwords, capnp id, a choice of a sequence

-
JS-NG> j.data.idgenerator.guid()                                                                    
+
JS-NG> j.data.idgenerator.guid()
 'c1d14970-f17f-49a3-aa85-f722013ee448'
 
-JS-NG> j.data.idgenerator.password(5)                                                               
+JS-NG> j.data.idgenerator.password(5)
 'b6~Sl'
 
-JS-NG> j.data.idgenerator.capnp_id()                                                                
+JS-NG> j.data.idgenerator.capnp_id()
 '0xa414b890b73d0940'
 
-JS-NG> j.data.idgenerator.random_int()                                                              
+JS-NG> j.data.idgenerator.random_int()
 7
 
-JS-NG> j.data.idgenerator.random_int(0, 5)                                                          
+JS-NG> j.data.idgenerator.random_int(0, 5)
 2
 
-Source code + +Expand source code +
"""idgenerator module helps with generating ids, guids, integers, chars, passwords, capnp id, a choice of a sequence
 
 ```
-JS-NG> j.data.idgenerator.guid()                                                                    
+JS-NG> j.data.idgenerator.guid()
 'c1d14970-f17f-49a3-aa85-f722013ee448'
 
-JS-NG> j.data.idgenerator.password(5)                                                               
+JS-NG> j.data.idgenerator.password(5)
 'b6~Sl'
 
-JS-NG> j.data.idgenerator.capnp_id()                                                                
+JS-NG> j.data.idgenerator.capnp_id()
 '0xa414b890b73d0940'
 
-JS-NG> j.data.idgenerator.random_int()                                                              
+JS-NG> j.data.idgenerator.random_int()
 7
 
-JS-NG> j.data.idgenerator.random_int(0, 5)                                                          
+JS-NG> j.data.idgenerator.random_int(0, 5)
 2
 ```
 """
@@ -163,17 +167,16 @@ 

Module jumpscale.data.idgenerator.idgenerator

Functions

-def capnp_id() +def capnp_id() ‑> str
-

Generates a valid id for a capnp schema.

+

Generates a valid id for a capnp schema.

Returns

-
-
strcapnp id
-
 
-
+

str – capnp id

-Source code + +Expand source code +
def capnp_id() -> str:
     """
     Generates a valid id for a capnp schema.
@@ -186,19 +189,18 @@ 

Returns

-def chars(nchars) +def chars(nchars: int) ‑> str
-

Gets a random string with length of n

+

Gets a random string with length of n

Arguments

nchars {int} – [description]

Returns

-
-
str – [description]
-
 
-
+

str – [description]

-Source code + +Expand source code +
def chars(nchars: int) -> str:
     """Gets a random string with length of n
 
@@ -213,17 +215,16 @@ 

Returns

-def guid() +def guid() ‑> str
-

Gets a uuid4

+

Gets a uuid4

Returns

-
-
struuid4
-
 
-
+

str – uuid4

-Source code + +Expand source code +
def guid() -> str:
     """Gets a uuid4
 
@@ -237,28 +238,29 @@ 

Returns

def incrementor_id()
-
+
-Source code + +Expand source code +
def incrementor_id():
     # user redis.incr.
     raise NotImplementedError()
-def nbytes(nbytes) +def nbytes(nbytes: int) ‑> bytearray
-

return bytearray of length n

+

return bytearray of length n

Arguments

nbytes {int} – number of bytes to generate

Returns

-
-
bytearrayresult
-
 
-
+

bytearray – result

-Source code + +Expand source code +
def nbytes(nbytes: int) -> bytearray:
     """return bytearray of length n
 
@@ -275,17 +277,19 @@ 

Returns

-def nfromchoices(n, choices) +def nfromchoices(n: int, choices: List[str]) ‑> str
-

Gets string from choices list

+

Gets string from choices list

Arguments

n {int} – how many choices {List[str]} – choices to pick from

Returns

-

str – joined result of n choices.

+

str – joined result of n choices.

-Source code + +Expand source code +
def nfromchoices(n: int, choices: List[str]) -> str:
     """Gets string from choices list
 
@@ -300,19 +304,18 @@ 

Returns

-def password(nchars) +def password(nchars: int) ‑> str
-

Return a password of length nchars

+

Return a password of length nchars

Arguments

nchars {int} – [description]

Returns

-
-
strpassword()
-
 
-
+

str – password

-Source code + +Expand source code +
def password(nchars: int) -> str:
     """Return a password of length nchars
 
@@ -327,20 +330,19 @@ 

Returns

-def random_int(from_=0, to=10) +def random_int(from_: int = 0, to: int = 10) ‑> int
-

Generate random int within range from_, to

+

Generate random int within range from_, to

Arguments

from_ {int} – lower limit, default 0 to {int} – upper limit

Returns

-
-
intrandom number
-
 
-
+

int – random number

-Source code + +Expand source code +
def random_int(from_: int = 0, to: int = 10) -> int:
     """Generate random int within range from_, to
 
@@ -386,9 +388,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/idgenerator/index.html b/docs/api/jumpscale/data/idgenerator/index.html index d74d8d944..0f6bf97c4 100644 --- a/docs/api/jumpscale/data/idgenerator/index.html +++ b/docs/api/jumpscale/data/idgenerator/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.idgenerator API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.idgenerator

-Source code + +Expand source code +
from .idgenerator import *
@@ -30,7 +34,7 @@

Sub-modules

jumpscale.data.idgenerator.idgenerator
-

idgenerator module helps with generating ids, guids, integers, chars, passwords, capnp id, a choice of a sequence …

+

idgenerator module helps with generating ids, guids, integers, chars, passwords, capnp id, a choice of a sequence …

@@ -61,9 +65,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/index.html b/docs/api/jumpscale/data/index.html index 6f8d5236d..a68fa6036 100644 --- a/docs/api/jumpscale/data/index.html +++ b/docs/api/jumpscale/data/index.html @@ -3,15 +3,17 @@ - + jumpscale.data API documentation - - - - - + + + + + + +
@@ -26,73 +28,73 @@

Sub-modules

jumpscale.data.bcdb
-
+
jumpscale.data.cache
-
+
jumpscale.data.encryption
-
+
jumpscale.data.fake
-
+
jumpscale.data.hash
-
+
jumpscale.data.idgenerator
-
+
jumpscale.data.inifile
-
+
jumpscale.data.nacl
-
+
jumpscale.data.platform
-

This module helps with getting all of the information of the platform related to the machine hosting js-ng …

+

This module helps with getting all of the information of the platform related to the machine hosting js-ng …

jumpscale.data.random_names
-

This module helps genearting random names, mainly for container names …

+

This module helps genearting random names, mainly for container names …

jumpscale.data.schema
-
+
jumpscale.data.serializers
-

This module does all the work for serialization/deserialization around pickle, base64, json, msgpack, pickle, dill, toml +

This module does all the work for serialization/deserialization around pickle, base64, json, msgpack, pickle, dill, toml ``` -JS-NG> obj = …

+JS-NG> obj = …

jumpscale.data.tarfile
-
+
jumpscale.data.terminaltable
-

This module helps around creation of terminal tables …

+

This module helps around creation of terminal tables …

jumpscale.data.text
-

helpers around string manipulation …

+

helpers around string manipulation …

jumpscale.data.time
-

Time helpers based on arrow …

+

Time helpers based on arrow …

jumpscale.data.types
-
+
@@ -139,9 +141,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/inifile/index.html b/docs/api/jumpscale/data/inifile/index.html index f4f86ad72..8cdcbcbc9 100644 --- a/docs/api/jumpscale/data/inifile/index.html +++ b/docs/api/jumpscale/data/inifile/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.inifile API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.inifile

-Source code + +Expand source code +
from .inifile import *
@@ -30,7 +34,7 @@

Sub-modules

jumpscale.data.inifile.inifile
-
+
@@ -61,9 +65,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/inifile/inifile.html b/docs/api/jumpscale/data/inifile/inifile.html index 779364475..914549b2a 100644 --- a/docs/api/jumpscale/data/inifile/inifile.html +++ b/docs/api/jumpscale/data/inifile/inifile.html @@ -3,15 +3,17 @@ - + jumpscale.data.inifile.inifile API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.inifile.inifile

-Source code + +Expand source code +
from configparser import ConfigParser
 
 
@@ -177,11 +181,13 @@ 

Classes

(path)
-

the IniFile object parses the content of the file provided by the path argument

+

the IniFile object parses the content of the file provided by the path argument

Args

-

path (str) : the path to file.ini

+

path (str) : the path to file.ini

-Source code + +Expand source code +
class IniFile:
     """
     the IniFile object parses the content of the file provided by the path argument
@@ -325,13 +331,15 @@ 

Methods

def add_property(self, section_name, property_key, property_value)
-

add new property to the file

+

add new property to the file

Args

section_name (str) : the section name property_key (str) : the name of the property -property_value (str) : the value of the property

+property_value (str) : the value of the property

-Source code + +Expand source code +
def add_property(self, section_name, property_key, property_value):
     """
     add new property to the file
@@ -347,11 +355,13 @@ 

Args

def add_section(self, section_name)
-

add an empty section to the file

+

add an empty section to the file

Args

-

section_name (str) : the section name

+

section_name (str) : the section name

-Source code + +Expand source code +
def add_section(self, section_name):
     """
     add an empty section to the file
@@ -365,17 +375,16 @@ 

Args

def check_property(self, section_name, property_name)
-

check the existence of the property in section

+

check the existence of the property in section

Args

section_name (str) : the name of the section where the property is property_name (str) : the property wanted to check

Returns

-
-
boolen expresion
-
 
-
+

boolen expresion

-Source code + +Expand source code +
def check_property(self, section_name, property_name):
     """
     check the existence of the property in section
@@ -392,16 +401,15 @@ 

Returns

def check_section(self, section_name)
-

check the existence of the section

+

check the existence of the section

Args

section_name (str) : the section wanted to check

Returns

-
-
boolen expresion
-
 
-
+

boolen expresion

-Source code + +Expand source code +
def check_section(self, section_name):
     """
     check the existence of the section
@@ -417,17 +425,16 @@ 

Returns

def get_boolen(self, section_name, property_name)
-

gat the value of property as boolen

+

gat the value of property as boolen

Args

section_name (str) : the name of the section where the property is property_name (str) : the property wanted to get its value

Returns

-
-
boolen of the value
-
 
-
+

boolen of the value

-Source code + +Expand source code +
def get_boolen(self, section_name, property_name):
     """
     gat the value of property as boolen
@@ -444,17 +451,16 @@ 

Returns

def get_float(self, section_name, property_name)
-

gat the value of property as float

+

gat the value of property as float

Args

section_name (str) : the name of the section where the property is property_name (str) : the property wanted to get its value

Returns

-
-
float of the value
-
 
-
+

float of the value

-Source code + +Expand source code +
def get_float(self, section_name, property_name):
     """
     gat the value of property as float
@@ -471,17 +477,16 @@ 

Returns

def get_int(self, section_name, property_name)
-

gat the value of property as int

+

gat the value of property as int

Args

section_name (str) : the name of the section where the property is property_name (str) : the property wanted to get its value

Returns

-
-
int of the value
-
 
-
+

int of the value

-Source code + +Expand source code +
def get_int(self, section_name, property_name):
     """
     gat the value of property as int
@@ -498,16 +503,15 @@ 

Returns

def get_properties(self, section_name)
-

get the properties name under the provided section

+

get the properties name under the provided section

Args

section_name (str) : the section name which contains the properties

Returns

-
-
list of all the properties under the section
-
 
-
+

list of all the properties under the section

-Source code + +Expand source code +
def get_properties(self, section_name):
     """
     get the properties name under the provided section
@@ -523,14 +527,13 @@ 

Returns

def get_sections(self)
-

search the file for sections names

+

search the file for sections names

Returns

-
-
a list of the sections
-
 
-
+

a list of the sections

-Source code + +Expand source code +
def get_sections(self):
     """
     search the file for sections names
@@ -544,17 +547,16 @@ 

Returns

def get_value(self, section_name, property_name)
-

gat the value of property as string

+

gat the value of property as string

Args

section_name (str) : the name of the section where the property is property_name (str) : the property wanted to get its value

Returns

-
-
string of the value
-
 
-
+

string of the value

-Source code + +Expand source code +
def get_value(self, section_name, property_name):
     """
     gat the value of property as string
@@ -571,12 +573,14 @@ 

Returns

def remove_property(self, section_name, property_name)
-

delete a property from section

+

delete a property from section

Args

section_name (str) : the section name -property_key (str) : the name of the property

+property_key (str) : the name of the property

-Source code + +Expand source code +
def remove_property(self, section_name, property_name):
     """
     delete a property from section
@@ -591,11 +595,13 @@ 

Args

def remove_section(self, section_name)
-

delete a section and its properties

+

delete a section and its properties

Args

-

section_name (str) : the section name

+

section_name (str) : the section name

-Source code + +Expand source code +
def remove_section(self, section_name):
     """
     delete a section and its properties
@@ -609,9 +615,11 @@ 

Args

def write(self)
-

apply all the changes to the file

+

apply all the changes to the file

-Source code + +Expand source code +
def write(self):
     """
     apply all the changes to the file
@@ -662,9 +670,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/nacl/index.html b/docs/api/jumpscale/data/nacl/index.html index f434e8579..b163331b7 100644 --- a/docs/api/jumpscale/data/nacl/index.html +++ b/docs/api/jumpscale/data/nacl/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.nacl API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.nacl

-Source code + +Expand source code +
from io import BytesIO
 from jumpscale.data.serializers.json import dumps
 from .jsnacl import *
@@ -51,7 +55,7 @@ 

Sub-modules

jumpscale.data.nacl.jsnacl
-
+
@@ -64,9 +68,11 @@

Functions

def payload_build(self, *args)
-

build a bytesIO buffer with all arguments serialized to somethign repeatable

+

build a bytesIO buffer with all arguments serialized to somethign repeatable

-Source code + +Expand source code +
def payload_build(self, *args):
     """
     build a bytesIO buffer with all arguments serialized to somethign repeatable
@@ -116,9 +122,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/nacl/jsnacl.html b/docs/api/jumpscale/data/nacl/jsnacl.html index f4bcc1ce5..36232fe5d 100644 --- a/docs/api/jumpscale/data/nacl/jsnacl.html +++ b/docs/api/jumpscale/data/nacl/jsnacl.html @@ -3,15 +3,17 @@ - + jumpscale.data.nacl.jsnacl API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.nacl.jsnacl

-Source code + +Expand source code +
import binascii
 import nacl.utils
 
@@ -197,16 +201,18 @@ 

Classes

(private_key=None, symmetric_key=None)
-

Constructor for nacl object

+

Constructor for nacl object

Args

private_key : bytes, optional
The private key used to sign and encrypt the data. Generated randomly if not given.
symmetric_key : bytes, optional
The key used for symmetric encryption. Generated randomly if not given.
-
+
-Source code + +Expand source code +
class NACL:
     KEY_SIZE = 32
 
@@ -362,7 +368,7 @@ 

Class variables

var KEY_SIZE
-
+

Methods

@@ -371,7 +377,7 @@

Methods

def decrypt(self, message, sender_public_key)
-

Decrypt a received message. (public key encryption)

+

Decrypt a received message. (public key encryption)

Args

message : bytes
@@ -381,11 +387,13 @@

Args

Returns

-
bytes
+
bytes
The decrypted message
-
+
-Source code + +Expand source code +
def decrypt(self, message, sender_public_key):
     """Decrypt a received message. (public key encryption)
 
@@ -403,7 +411,7 @@ 

Returns

def decrypt_symmetric(self, message, symmetric_key)
-

Decrypt the receiver message. (secret key encryption)

+

Decrypt the receiver message. (secret key encryption)

Args

message : bytes
@@ -411,11 +419,13 @@

Args

Returns

-
bytes
+
bytes
The decrypted message
-
+
-Source code + +Expand source code +
def decrypt_symmetric(self, message, symmetric_key):
     """Decrypt the receiver message. (secret key encryption)
 
@@ -432,7 +442,7 @@ 

Returns

def encrypt(self, message, reciever_public_key)
-

Encrypt the message to send to a receiver. (public key encryption)

+

Encrypt the message to send to a receiver. (public key encryption)

Args

message : bytes
@@ -442,11 +452,13 @@

Args

Returns

-
bytes
+
bytes
The encrypted message
-
+
-Source code + +Expand source code +
def encrypt(self, message, reciever_public_key):
     """Encrypt the message to send to a receiver. (public key encryption)
 
@@ -464,7 +476,7 @@ 

Returns

def encrypt_symmetric(self, message)
-

Encrypt the message to send to a receiver. (secret key encryption)

+

Encrypt the message to send to a receiver. (secret key encryption)

Args

message : bytes
@@ -472,11 +484,13 @@

Args

Returns

-
bytes
+
bytes
The encrypted message
-
+
-Source code + +Expand source code +
def encrypt_symmetric(self, message):
     """Encrypt the message to send to a receiver. (secret key encryption)
 
@@ -493,14 +507,16 @@ 

Returns

def get_private_key(self)
-

Getter for the private key.

+

Getter for the private key.

Returns

-
bytes
+
bytes
The private key.
-
+
-Source code + +Expand source code +
def get_private_key(self):
     """Getter for the private key.
 
@@ -514,14 +530,16 @@ 

Returns

def get_public_key(self)
-

Getter for the public key.

+

Getter for the public key.

Returns

-
bytes
+
bytes
The public key.
-
+
-Source code + +Expand source code +
def get_public_key(self):
     """Getter for the public key.
 
@@ -535,9 +553,11 @@ 

Returns

def get_public_key_hex(self)
-
+
-Source code + +Expand source code +
def get_public_key_hex(self):
     return binascii.hexlify(self.public_key.encode()).decode()
@@ -546,14 +566,16 @@

Returns

def get_signing_seed(self)
-

Returns the signing seed (same as the private key).

+

Returns the signing seed (same as the private key).

Returns

-
bytes
+
bytes
The 32-bit signing key.
-
+
-Source code + +Expand source code +
def get_signing_seed(self):
     """Returns the signing seed (same as the private key).
 
@@ -567,14 +589,16 @@ 

Returns

def get_symmetric_key(self)
-

Getter for the symmetric key.

+

Getter for the symmetric key.

Returns

-
bytes
+
bytes
The symmetric key.
-
+
-Source code + +Expand source code +
def get_symmetric_key(self):
     """Getter for the symmetric key.
 
@@ -588,14 +612,16 @@ 

Returns

def get_verification_key(self)
-

Returns the verification key.

+

Returns the verification key.

Returns

-
bytes
+
bytes
The verification key.
-
+
-Source code + +Expand source code +
def get_verification_key(self):
     """Returns the verification key.
 
@@ -609,9 +635,11 @@ 

Returns

def get_verify_key_hex(self)
-
+
-Source code + +Expand source code +
def get_verify_key_hex(self):
     return binascii.hexlify(self.verify_key.encode()).decode()
@@ -620,16 +648,21 @@

Returns

def sign(self, message)
-

Sign the message and return the messsage and the signature.

+

Sign the message and return the messsage and the signature.

Args

message : bytes
The message to be signed

Returns

-

bytes, bytes: The message and the signature.

+
+
bytes, bytes
+
The message and the signature.
+
-Source code + +Expand source code +
def sign(self, message):
     """Sign the message and return the messsage and the signature.
 
@@ -647,7 +680,7 @@ 

Returns

def sign_hex(self, message)
-

Sign the message and return the messsage and the signature.

+

Sign the message and return the messsage and the signature.

Args

message : bytes
@@ -655,11 +688,13 @@

Args

Returns

-
bytes
+
bytes
The message and the signature.
-
+
-Source code + +Expand source code +
def sign_hex(self, message):
     """Sign the message and return the messsage and the signature.
 
@@ -678,7 +713,7 @@ 

Returns

def verify(self, message, signature, verification_key)
-

Verify that the signature using the verification key

+

Verify that the signature using the verification key

Args

message : bytes
@@ -690,11 +725,13 @@

Args

Returns

-
bool
+
bool
True if the verification succeeds.
-
+
-Source code + +Expand source code +
def verify(self, message, signature, verification_key):
     """Verify that the signature using the verification key
 
@@ -757,9 +794,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/platform/index.html b/docs/api/jumpscale/data/platform/index.html index fd8ac1b0e..0b7e62557 100644 --- a/docs/api/jumpscale/data/platform/index.html +++ b/docs/api/jumpscale/data/platform/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.platform API documentation - - - - - + + + + + + +
@@ -81,7 +83,9 @@

Get the complete profile info

-Source code + +Expand source code +
"""This module helps with getting all of the information of the platform related to the machine hosting js-ng
 
 
@@ -168,7 +172,7 @@ 

Get the complete profile info

import os import sys import time -import pprint +import json import random import socket import struct @@ -367,63 +371,13 @@

Get the complete profile info

return ret -_real_safe_repr = pprint._safe_repr - - -def _fake_json_dumps(val, indent=2): - # never do this. this is a hack for Python 2.4. Python 2.5 added - # the json module for a reason. - def _fake_safe_repr(*a, **kw): - res, is_read, is_rec = _real_safe_repr(*a, **kw) - if res == "None": - res = "null" - if res == "True": - res = "true" - if res == "False": - res = "false" - if not (res.startswith("'") or res.startswith("u'")): - res = res - else: - if res.startswith("u"): - res = res[1:] - - contents = res[1:-1] - contents = contents.replace('"', "").replace(r"\"", "") - res = '"' + contents + '"' - return res, is_read, is_rec - - pprint._safe_repr = _fake_safe_repr - try: - ret = pprint.pformat(val, indent=indent) - finally: - pprint._safe_repr = _real_safe_repr - - return ret - - def get_profile_json(indent=False): + data_dict = get_profile() + if indent: - indent = 2 + return json.dumps(data_dict, sort_keys=True, indent=2) else: - indent = 0 - try: - import json - - def dumps(val, indent): - if indent: - return json.dumps(val, sort_keys=True, indent=indent) - return json.dumps(val, sort_keys=True) - - except ImportError: - - def dumps(val, indent): - ret = _fake_json_dumps(val, indent=indent) - if not indent: - ret = re.sub("\n\s*", " ", ret) - return ret - - data_dict = get_profile() - return dumps(data_dict, indent) + return json.dumps(data_dict, sort_keys=True) def _escape_shell_args(args, sep=" ", style=None): @@ -531,20 +485,22 @@

Functions

def get_profile(scrub=False)
-

The main entrypoint to platform. Calling this will return a +

The main entrypoint to platform. Calling this will return a JSON-serializable dictionary of information about the current process. It is very unlikely that the information returned will change during the lifetime of the process, and in most cases the majority of the information stays the same between runs as well. -:func:get_profile() takes one optional keyword argument, scrub, +:func:get_profile() takes one optional keyword argument, scrub, a :class:bool that, if True, blanks out identifiable information. This includes current working directory, hostname, Python executable path, command-line arguments, and username. Values are replaced with '-', but for compatibility keys -remain in place.

+remain in place.

-Source code + +Expand source code +
def get_profile(scrub=False):
     """The main entrypoint to platform. Calling this will return a
     JSON-serializable dictionary of information about the current
@@ -610,41 +566,29 @@ 

Functions

def get_profile_json(indent=False)
-
+
-Source code + +Expand source code +
def get_profile_json(indent=False):
+    data_dict = get_profile()
+
     if indent:
-        indent = 2
+        return json.dumps(data_dict, sort_keys=True, indent=2)
     else:
-        indent = 0
-    try:
-        import json
-
-        def dumps(val, indent):
-            if indent:
-                return json.dumps(val, sort_keys=True, indent=indent)
-            return json.dumps(val, sort_keys=True)
-
-    except ImportError:
-
-        def dumps(val, indent):
-            ret = _fake_json_dumps(val, indent=indent)
-            if not indent:
-                ret = re.sub("\n\s*", " ", ret)
-            return ret
-
-    data_dict = get_profile()
-    return dumps(data_dict, indent)
+ return json.dumps(data_dict, sort_keys=True)
def get_python_info()
-
+
-Source code + +Expand source code +
def get_python_info():
     ret = {}
     ret["argv"] = _escape_shell_args(sys.argv)
@@ -680,9 +624,11 @@ 

Functions

def is_linux()
-
+
-Source code + +Expand source code +
def is_linux():
     return sys.platform.lower() == "linux"
@@ -691,9 +637,11 @@

Functions

def is_osx()
-
+
-Source code + +Expand source code +
def is_osx():
     return sys.platform.lower() == "darwin"
@@ -702,9 +650,11 @@

Functions

def is_unix()
-
+
-Source code + +Expand source code +
def is_unix():
     return is_linux() or is_osx()
@@ -746,9 +696,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/random_names/index.html b/docs/api/jumpscale/data/random_names/index.html index 25d2e0502..fc560f5e3 100644 --- a/docs/api/jumpscale/data/random_names/index.html +++ b/docs/api/jumpscale/data/random_names/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.random_names API documentation - - - - - + + + + + + +
@@ -21,21 +23,23 @@

Module jumpscale.data.random_names

This module helps genearting random names, mainly for container names.

-
JS-NG> j.data.random_names.random_name()                                                            
+
JS-NG> j.data.random_names.random_name()
 'loving_borg'
 
-JS-NG> j.data.random_names.random_name()                                                            
+JS-NG> j.data.random_names.random_name()
 'quirky_wiles'
 
-Source code + +Expand source code +
"""This module helps genearting random names, mainly for container names.
 
 ```
-JS-NG> j.data.random_names.random_name()                                                            
+JS-NG> j.data.random_names.random_name()
 'loving_borg'
 
-JS-NG> j.data.random_names.random_name()                                                            
+JS-NG> j.data.random_names.random_name()
 'quirky_wiles'
 ```
 
@@ -228,9 +232,11 @@ 

Functions

def random_name()
-

Returns a random name "first name" & "last name

+

Returns a random name "first name" & "last name

-Source code + +Expand source code +
def random_name():
     """ Returns a random name "first name" & "last name """
 
@@ -264,9 +270,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/schema/index.html b/docs/api/jumpscale/data/schema/index.html index 5ff99ae27..72936a644 100644 --- a/docs/api/jumpscale/data/schema/index.html +++ b/docs/api/jumpscale/data/schema/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.schema API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.schema

-Source code + +Expand source code +
from .schema import *
@@ -30,7 +34,7 @@

Sub-modules

jumpscale.data.schema.schema
-
+

@@ -61,9 +65,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/schema/schema.html b/docs/api/jumpscale/data/schema/schema.html index f9d63a493..772a2448d 100644 --- a/docs/api/jumpscale/data/schema/schema.html +++ b/docs/api/jumpscale/data/schema/schema.html @@ -3,15 +3,17 @@ - + jumpscale.data.schema.schema API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.schema.schema

-Source code + +Expand source code +
import re
 from jumpscale.loader import j
 
@@ -299,7 +303,7 @@ 

Functions

def parse_schema(text)
-

Parses a schema from string.

+

Parses a schema from string.

Args

text : str
@@ -307,16 +311,18 @@

Args

Raises

-
RuntimeError
+
RuntimeError
Thrown when the schema can't be parsed.

Returns

-
Schema
+
Schema
Schema object representing the schema.
-
+
-Source code + +Expand source code +
def parse_schema(text):
     """Parses a schema from string.
 
@@ -352,9 +358,11 @@ 

Returns

def pascalify_name(name)
-
+
-Source code + +Expand source code +
def pascalify_name(name):
     return "".join([x.capitalize() for x in name.replace("_", "-").split("-")])
@@ -368,9 +376,11 @@

Classes

class Property
-
+
-Source code + +Expand source code +
class Property:
     def __init__(self):
         self.index = False
@@ -409,9 +419,11 @@ 

Static methods

def get_id_prop()
-
+
-Source code + +Expand source code +
@classmethod
 def get_id_prop(cls):
     prop = cls()
@@ -426,9 +438,11 @@ 

Instance variables

var url_to_class_name
-
+
-Source code + +Expand source code +
@property
 def url_to_class_name(self):
     url = self.defaultvalue
@@ -442,9 +456,11 @@ 

Instance variables

class Schema
-
+
-Source code + +Expand source code +
class Schema:
     def __init__(self):
         self.url = ""
@@ -478,9 +494,11 @@ 

Instance variables

var url_to_class_name
-
+
-Source code + +Expand source code +
@property
 def url_to_class_name(self):
     return "".join(x.capitalize() for x in self.url.split("."))
@@ -493,9 +511,11 @@

Methods

def get_classes_required(self)
-
+
-Source code + +Expand source code +
def get_classes_required(self):
     classes = []
     for prop_name, prop in self.props.items():
@@ -508,9 +528,11 @@ 

Methods

def get_dependencies(self)
-
+
-Source code + +Expand source code +
def get_dependencies(self):
 
     return {enum: self.get_enums_required(), classes: self.get_classes_requireds()}
@@ -520,9 +542,11 @@

Methods

def get_enums_required(self)
-
+
-Source code + +Expand source code +
def get_enums_required(self):
     enums = []
     for prop_name, prop in self.props.items():
@@ -578,9 +602,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/base64.html b/docs/api/jumpscale/data/serializers/base64.html index a13d4607b..8ca88ae5c 100644 --- a/docs/api/jumpscale/data/serializers/base64.html +++ b/docs/api/jumpscale/data/serializers/base64.html @@ -3,15 +3,17 @@ - + jumpscale.data.serializers.base64 API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.serializers.base64

-Source code + +Expand source code +
import base64
 
 
@@ -66,13 +70,15 @@ 

Functions

def decode(b)
-

decode base64 bytes to original obj

+

decode base64 bytes to original obj

Arguments

b (bytes) : the bytes will be decoded

Returns

-

(string) : the decoded string

+

(string) : the decoded string

-Source code + +Expand source code +
def decode(b):
     """decode base64 bytes to original obj
 
@@ -91,16 +97,18 @@ 

Returns

def encode(s)
-

encode string with base64 algorithm

+

encode string with base64 algorithm

Arguments

s (string) : the string will be encoded

Returns

-
bytes : the encoded bytes
-
 
-
+
bytes
+
the encoded bytes
+

-Source code + +Expand source code +
def encode(s):
     """encode string with base64 algorithm
 
@@ -143,9 +151,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/dill.html b/docs/api/jumpscale/data/serializers/dill.html index df3e31e13..ed9d9753a 100644 --- a/docs/api/jumpscale/data/serializers/dill.html +++ b/docs/api/jumpscale/data/serializers/dill.html @@ -3,15 +3,17 @@ - + jumpscale.data.serializers.dill API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.serializers.dill

-Source code + +Expand source code +
from dill import dumps, dump, loads, load
@@ -49,9 +53,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/index.html b/docs/api/jumpscale/data/serializers/index.html index e93d754b5..65b75443c 100644 --- a/docs/api/jumpscale/data/serializers/index.html +++ b/docs/api/jumpscale/data/serializers/index.html @@ -3,17 +3,19 @@ - + jumpscale.data.serializers API documentation - - - - - + + + + + + +
@@ -23,18 +25,18 @@

Module jumpscale.data.serializers

This module does all the work for serialization/deserialization around pickle, base64, json, msgpack, pickle, dill, toml

-
JS-NG> obj = {"name":"username", "list":[1,3,4,7], "n":5}                                           
+
JS-NG> obj = {"name":"username", "list":[1,3,4,7], "n":5}
 
-JS-NG> j.data.serializers.json.dumps(obj)                                                           
+JS-NG> j.data.serializers.json.dumps(obj)
 '{"name": "username", "list": [1, 3, 4, 7], "n": 5}'
 
-JS-NG> j.data.serializers.toml.dumps(obj)                                                           
+JS-NG> j.data.serializers.toml.dumps(obj)
 'name = "username"
 list = [1, 3, 4, 7]
 n = 5
 '
 
-JS-NG> j.data.serializers.yaml.dumps(obj)                                                           
+JS-NG> j.data.serializers.yaml.dumps(obj)
 'list:
 - 1
 - 3
@@ -44,25 +46,27 @@ 

Module jumpscale.data.serializers

name: username ' -JS-NG> j.data.serializers.msgpack.dumps(obj) +JS-NG> j.data.serializers.msgpack.dumps(obj) b'ƒ¤name¨username¤list”¡n'
-Source code + +Expand source code +
"""This module does all the work for serialization/deserialization around pickle, base64, json, msgpack, pickle, dill, toml
 ```
-JS-NG> obj = {"name":"username", "list":[1,3,4,7], "n":5}                                           
+JS-NG> obj = {"name":"username", "list":[1,3,4,7], "n":5}
 
-JS-NG> j.data.serializers.json.dumps(obj)                                                           
+JS-NG> j.data.serializers.json.dumps(obj)
 '{"name": "username", "list": [1, 3, 4, 7], "n": 5}'
 
-JS-NG> j.data.serializers.toml.dumps(obj)                                                           
+JS-NG> j.data.serializers.toml.dumps(obj)
 'name = "username"\nlist = [1, 3, 4, 7]\nn = 5\n'
 
-JS-NG> j.data.serializers.yaml.dumps(obj)                                                           
+JS-NG> j.data.serializers.yaml.dumps(obj)
 'list:\n- 1\n- 3\n- 4\n- 7\nn: 5\nname: username\n'
 
-JS-NG> j.data.serializers.msgpack.dumps(obj)                                                        
+JS-NG> j.data.serializers.msgpack.dumps(obj)
 b'\x83\xa4name\xa8username\xa4list\x94\x01\x03\x04\x07\xa1n\x05'
 ```
 """
@@ -82,35 +86,35 @@ 

Sub-modules

jumpscale.data.serializers.base64
-
+
jumpscale.data.serializers.dill
-
+
jumpscale.data.serializers.json
-
+
jumpscale.data.serializers.lzma
-
+
jumpscale.data.serializers.msgpack
-
+
jumpscale.data.serializers.pickle
-
+
jumpscale.data.serializers.toml
-
+
jumpscale.data.serializers.yaml
-
+
@@ -148,9 +152,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/json.html b/docs/api/jumpscale/data/serializers/json.html index fada1115c..7500d904b 100644 --- a/docs/api/jumpscale/data/serializers/json.html +++ b/docs/api/jumpscale/data/serializers/json.html @@ -3,15 +3,17 @@ - + jumpscale.data.serializers.json API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.serializers.json

-Source code + +Expand source code +
import json
 
 
@@ -84,16 +88,18 @@ 

Functions

def dump_to_file(file_path, obj)
-

Writes the dumped obj to a file

+

Writes the dumped obj to a file

Args

file_path : str
path to write to
obj : dict
the dict which will be dumped
-
+
-Source code + +Expand source code +
def dump_to_file(file_path, obj):
     """Writes the dumped obj to a file
 
@@ -109,16 +115,18 @@ 

Args

def dumps(obj)
-

dump dict object into json stream

+

dump dict object into json stream

Arguments

obj (dict) : the dict which will be dumped

Returns

-
string : the json stream
-
 
-
+
string
+
the json stream
+
-Source code + +Expand source code +
def dumps(obj):
     """dump dict object into json stream
 
@@ -135,14 +143,16 @@ 

Returns

def load_from_file(file_path)
-

Loads data from file to a dict

+

Loads data from file to a dict

Args

file_path : str
path of the json file
-
+
-Source code + +Expand source code +
def load_from_file(file_path):
     """Loads data from file to a dict
 
@@ -158,16 +168,18 @@ 

Args

def loads(s)
-

loads the data from json string into dict

+

loads the data from json string into dict

Arguments

s (string) : the json stream

Returns

-
dict : the loaded data from json stram
-
 
-
+
dict
+
the loaded data from json stram
+
-Source code + +Expand source code +
def loads(s):
     """loads the data from json string into dict
 
@@ -210,9 +222,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/lzma.html b/docs/api/jumpscale/data/serializers/lzma.html index 70e5c561a..1ab1838d6 100644 --- a/docs/api/jumpscale/data/serializers/lzma.html +++ b/docs/api/jumpscale/data/serializers/lzma.html @@ -3,15 +3,17 @@ - + jumpscale.data.serializers.lzma API documentation - - - - - + + + + + + +
@@ -21,15 +23,17 @@

Module jumpscale.data.serializers.lzma

-Source code + +Expand source code +
import pylzma
 
 def compress(obj):
     """compress string with lzma algorithm
-    
+
     Arguments:
         obj (string) : the string will be encoded
-    
+
     Returns:
         bytes : the compressed bytes
     """
@@ -37,10 +41,10 @@ 

Module jumpscale.data.serializers.lzma

def decompress(s): """decompress lzma bytes to original obj - + Arguments: s (bytes) : the bytes will be compressed - + Returns: (string) : the decompressed string """ @@ -58,22 +62,24 @@

Functions

def compress(obj)
-

compress string with lzma algorithm

+

compress string with lzma algorithm

Arguments

obj (string) : the string will be encoded

Returns

-
bytes : the compressed bytes
-
 
-
+
bytes
+
the compressed bytes
+
-Source code + +Expand source code +
def compress(obj):
     """compress string with lzma algorithm
-    
+
     Arguments:
         obj (string) : the string will be encoded
-    
+
     Returns:
         bytes : the compressed bytes
     """
@@ -84,19 +90,21 @@ 

Returns

def decompress(s)
-

decompress lzma bytes to original obj

+

decompress lzma bytes to original obj

Arguments

s (bytes) : the bytes will be compressed

Returns

-

(string) : the decompressed string

+

(string) : the decompressed string

-Source code + +Expand source code +
def decompress(s):
     """decompress lzma bytes to original obj
-    
+
     Arguments:
         s (bytes) : the bytes will be compressed
-    
+
     Returns:
         (string) : the decompressed string
     """
@@ -129,9 +137,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/msgpack.html b/docs/api/jumpscale/data/serializers/msgpack.html index 2954160cf..bff4401c2 100644 --- a/docs/api/jumpscale/data/serializers/msgpack.html +++ b/docs/api/jumpscale/data/serializers/msgpack.html @@ -3,15 +3,17 @@ - + jumpscale.data.serializers.msgpack API documentation - - - - - + + + + + + +
@@ -21,16 +23,18 @@

Module jumpscale.data.serializers.msgpack

-Source code + +Expand source code +
import msgpack
 
 
 def dumps(obj):
-    """dump dict object into msgpack stream 
-    
+    """dump dict object into msgpack stream
+
     Arguments:
-        obj (dict) : the dict which will be dumped     
-    
+        obj (dict) : the dict which will be dumped
+
     Returns:
         string : the msgpack stream
     """
@@ -39,10 +43,10 @@ 

Module jumpscale.data.serializers.msgpack

def loads(s): """loads the data from msgpack string into dict - + Arguments: s (string) : the msgpack stream - + Returns: dict : the loaded data from msgpack stram """ @@ -62,23 +66,25 @@

Functions

def dumps(obj)
-

dump dict object into msgpack stream

+

dump dict object into msgpack stream

Arguments

obj (dict) : the dict which will be dumped

Returns

-
string : the msgpack stream
-
 
-
+
string
+
the msgpack stream
+
-Source code + +Expand source code +
def dumps(obj):
-    """dump dict object into msgpack stream 
-    
+    """dump dict object into msgpack stream
+
     Arguments:
-        obj (dict) : the dict which will be dumped     
-    
+        obj (dict) : the dict which will be dumped
+
     Returns:
         string : the msgpack stream
     """
@@ -89,22 +95,24 @@ 

Returns

def loads(s)
-

loads the data from msgpack string into dict

+

loads the data from msgpack string into dict

Arguments

s (string) : the msgpack stream

Returns

-
dict : the loaded data from msgpack stram
-
 
-
+
dict
+
the loaded data from msgpack stram
+
-Source code + +Expand source code +
def loads(s):
     """loads the data from msgpack string into dict
-    
+
     Arguments:
         s (string) : the msgpack stream
-    
+
     Returns:
         dict : the loaded data from msgpack stram
     """
@@ -139,9 +147,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/pickle.html b/docs/api/jumpscale/data/serializers/pickle.html index aa12707f3..67f0663b1 100644 --- a/docs/api/jumpscale/data/serializers/pickle.html +++ b/docs/api/jumpscale/data/serializers/pickle.html @@ -3,15 +3,17 @@ - + jumpscale.data.serializers.pickle API documentation - - - - - + + + + + + +
@@ -21,16 +23,18 @@

Module jumpscale.data.serializers.pickle

-Source code + +Expand source code +
import pickle
 
 
 def decompress(obj):
-    """dump pickle bytes object into string 
-    
+    """dump pickle bytes object into string
+
     Arguments:
-        obj (pickle bytes) : the pickle bytes which will be dumped     
-    
+        obj (pickle bytes) : the pickle bytes which will be dumped
+
     Returns:
         string : the string
     """
@@ -39,10 +43,10 @@ 

Module jumpscale.data.serializers.pickle

def compress(obj): """loads the data from pickle string into pickle bytes - + Arguments: obj (string) : the string - + Returns: pickle bytes : the loaded data from pickle stram """ @@ -60,22 +64,24 @@

Functions

def compress(obj)
-

loads the data from pickle string into pickle bytes

+

loads the data from pickle string into pickle bytes

Arguments

obj (string) : the string

Returns

-
pickle bytes : the loaded data from pickle stram
-
 
-
+
pickle bytes
+
the loaded data from pickle stram
+
-Source code + +Expand source code +
def compress(obj):
     """loads the data from pickle string into pickle bytes
-    
+
     Arguments:
         obj (string) : the string
-    
+
     Returns:
         pickle bytes : the loaded data from pickle stram
     """
@@ -86,23 +92,25 @@ 

Returns

def decompress(obj)
-

dump pickle bytes object into string

+

dump pickle bytes object into string

Arguments

obj (pickle bytes) : the pickle bytes which will be dumped

Returns

-
string : the string
-
 
-
+
string
+
the string
+
-Source code + +Expand source code +
def decompress(obj):
-    """dump pickle bytes object into string 
-    
+    """dump pickle bytes object into string
+
     Arguments:
-        obj (pickle bytes) : the pickle bytes which will be dumped     
-    
+        obj (pickle bytes) : the pickle bytes which will be dumped
+
     Returns:
         string : the string
     """
@@ -135,9 +143,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/toml.html b/docs/api/jumpscale/data/serializers/toml.html index 910a0f23b..e6fb92892 100644 --- a/docs/api/jumpscale/data/serializers/toml.html +++ b/docs/api/jumpscale/data/serializers/toml.html @@ -3,15 +3,17 @@ - + jumpscale.data.serializers.toml API documentation - - - - - + + + + + + +
@@ -21,16 +23,18 @@

Module jumpscale.data.serializers.toml

-Source code + +Expand source code +
import pytoml
 
 
 def dumps(d):
-    """dump dict object into toml stream 
-    
+    """dump dict object into toml stream
+
     Arguments:
-        d (dict) : the dict which will be dumped     
-    
+        d (dict) : the dict which will be dumped
+
     Returns:
         string : the toml stream
     """
@@ -40,10 +44,10 @@ 

Module jumpscale.data.serializers.toml

def loads(s): """loads the data from toml string into dict - + Arguments: s (string) : the toml stream - + Returns: dict : the loaded data from toml stram """ @@ -62,23 +66,25 @@

Functions

def dumps(d)
-

dump dict object into toml stream

+

dump dict object into toml stream

Arguments

d (dict) : the dict which will be dumped

Returns

-
string : the toml stream
-
 
-
+
string
+
the toml stream
+
-Source code + +Expand source code +
def dumps(d):
-    """dump dict object into toml stream 
-    
+    """dump dict object into toml stream
+
     Arguments:
-        d (dict) : the dict which will be dumped     
-    
+        d (dict) : the dict which will be dumped
+
     Returns:
         string : the toml stream
     """
@@ -90,22 +96,24 @@ 

Returns

def loads(s)
-

loads the data from toml string into dict

+

loads the data from toml string into dict

Arguments

s (string) : the toml stream

Returns

-
dict : the loaded data from toml stram
-
 
-
+
dict
+
the loaded data from toml stram
+
-Source code + +Expand source code +
def loads(s):
     """loads the data from toml string into dict
-    
+
     Arguments:
         s (string) : the toml stream
-    
+
     Returns:
         dict : the loaded data from toml stram
     """
@@ -139,9 +147,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/serializers/yaml.html b/docs/api/jumpscale/data/serializers/yaml.html index d087d3163..e5056aa0b 100644 --- a/docs/api/jumpscale/data/serializers/yaml.html +++ b/docs/api/jumpscale/data/serializers/yaml.html @@ -3,15 +3,17 @@ - + jumpscale.data.serializers.yaml API documentation - - - - - + + + + + + +
@@ -21,16 +23,18 @@

Module jumpscale.data.serializers.yaml

-Source code + +Expand source code +
import yaml
 
 
 def dumps(obj):
-    """dump dict object into yaml stream 
-    
+    """dump dict object into yaml stream
+
     Arguments:
-        obj (dict) : the dict which will be dumped     
-    
+        obj (dict) : the dict which will be dumped
+
     Returns:
         string : the yaml stream
     """
@@ -39,10 +43,10 @@ 

Module jumpscale.data.serializers.yaml

def loads(s): """loads the data from yaml string into dict - + Arguments: s (string) : the yaml stream - + Returns: dict : the loaded data from yaml stram """ @@ -60,23 +64,25 @@

Functions

def dumps(obj)
-

dump dict object into yaml stream

+

dump dict object into yaml stream

Arguments

obj (dict) : the dict which will be dumped

Returns

-
string : the yaml stream
-
 
-
+
string
+
the yaml stream
+
-Source code + +Expand source code +
def dumps(obj):
-    """dump dict object into yaml stream 
-    
+    """dump dict object into yaml stream
+
     Arguments:
-        obj (dict) : the dict which will be dumped     
-    
+        obj (dict) : the dict which will be dumped
+
     Returns:
         string : the yaml stream
     """
@@ -87,22 +93,24 @@ 

Returns

def loads(s)
-

loads the data from yaml string into dict

+

loads the data from yaml string into dict

Arguments

s (string) : the yaml stream

Returns

-
dict : the loaded data from yaml stram
-
 
-
+
dict
+
the loaded data from yaml stram
+
-Source code + +Expand source code +
def loads(s):
     """loads the data from yaml string into dict
-    
+
     Arguments:
         s (string) : the yaml stream
-    
+
     Returns:
         dict : the loaded data from yaml stram
     """
@@ -135,9 +143,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/tarfile/index.html b/docs/api/jumpscale/data/tarfile/index.html index ef29627ba..dc82c0cba 100644 --- a/docs/api/jumpscale/data/tarfile/index.html +++ b/docs/api/jumpscale/data/tarfile/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.tarfile API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.tarfile

-Source code + +Expand source code +
from .tar_file import *
@@ -30,7 +34,7 @@

Sub-modules

jumpscale.data.tarfile.tar_file
-
+
@@ -61,9 +65,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/tarfile/tar_file.html b/docs/api/jumpscale/data/tarfile/tar_file.html index 13651f9e7..3d715f8e4 100644 --- a/docs/api/jumpscale/data/tarfile/tar_file.html +++ b/docs/api/jumpscale/data/tarfile/tar_file.html @@ -3,15 +3,17 @@ - + jumpscale.data.tarfile.tar_file API documentation - - - - - + + + + + + +
@@ -21,13 +23,15 @@

Module jumpscale.data.tarfile.tar_file

-Source code + +Expand source code +
import tarfile
 
 
 def istar(path):
     """check if the file is .tar format
-    
+
     Arguments:
         path (str) : the path for the file
     """
@@ -36,7 +40,7 @@ 

Module jumpscale.data.tarfile.tar_file

def compress(source, output): """make an archive file from directory or file - + Arguments: source (str) : the path for the file or the directory output (str) : the path for the output @@ -47,7 +51,7 @@

Module jumpscale.data.tarfile.tar_file

class Reader: """handle the reading operation on tar file - + Arguments: path (str) : the path for tar file """ @@ -86,15 +90,17 @@

Functions

def compress(source, output)
-

make an archive file from directory or file

+

make an archive file from directory or file

Arguments

source (str) : the path for the file or the directory -output (str) : the path for the output

+output (str) : the path for the output

-Source code + +Expand source code +
def compress(source, output):
     """make an archive file from directory or file
-    
+
     Arguments:
         source (str) : the path for the file or the directory
         output (str) : the path for the output
@@ -107,14 +113,16 @@ 

Arguments

def istar(path)
-

check if the file is .tar format

+

check if the file is .tar format

Arguments

-

path (str) : the path for the file

+

path (str) : the path for the file

-Source code + +Expand source code +
def istar(path):
     """check if the file is .tar format
-    
+
     Arguments:
         path (str) : the path for the file
     """
@@ -131,14 +139,16 @@ 

Classes

(path)
-

handle the reading operation on tar file

+

handle the reading operation on tar file

Arguments

-

path (str) : the path for tar file

+

path (str) : the path for tar file

-Source code + +Expand source code +
class Reader:
     """handle the reading operation on tar file
-    
+
     Arguments:
         path (str) : the path for tar file
     """
@@ -171,11 +181,13 @@ 

Methods

def extract(self, output)
-

extract all the files from the archive to a directory.

+

extract all the files from the archive to a directory.

Args

-

output (str) : the path for the output folder

+

output (str) : the path for the output folder

-Source code + +Expand source code +
def extract(self, output):
     """extract all the files from the archive to a directory.
     Args:
@@ -188,9 +200,11 @@ 

Args

def get_content(self)
-

get list of the content in tar file

+

get list of the content in tar file

-Source code + +Expand source code +
def get_content(self):
     """get list of the content in tar file"""
     return self.file.getnames()
@@ -233,9 +247,7 @@

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/terminaltable/index.html b/docs/api/jumpscale/data/terminaltable/index.html index 8742a88a1..773c49f7f 100644 --- a/docs/api/jumpscale/data/terminaltable/index.html +++ b/docs/api/jumpscale/data/terminaltable/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.terminaltable API documentation - - - - - + + + + + + +
@@ -48,10 +50,12 @@

Module jumpscale.data.terminaltable

| xmonader | +----+----------+

-Source code + +Expand source code +
"""This module helps around creation of terminal tables
 
-JS-NG> j.data.terminaltable.print_table("users", [ ["id", "name"], ["1", "ahmed"], ["2", "xmonader"]])           
+JS-NG> j.data.terminaltable.print_table("users", [ ["id", "name"], ["1", "ahmed"], ["2", "xmonader"]])
 +users----------+
 | id | name     |
 +----+----------+
@@ -59,8 +63,8 @@ 

Module jumpscale.data.terminaltable

| 2 | xmonader | +----+----------+ -JS-NG> tbl = j.data.terminaltable.create("users", [ ["id", "name"], ["1", "ahmed"], ["2", "xmonader"]]) -JS-NG> print(tbl) +JS-NG> tbl = j.data.terminaltable.create("users", [ ["id", "name"], ["1", "ahmed"], ["2", "xmonader"]]) +JS-NG> print(tbl) +users----------+ | id | name | +----+----------+ @@ -96,12 +100,14 @@

Module jumpscale.data.terminaltable

Functions

-def create(title, data, type_='ascii') +def create(title, data, type_='ascii')
-
+
-Source code + +Expand source code +
def create(title, data, type_="ascii"):
 
     table_type = TABLE_TYPES.get(type_)
@@ -114,12 +120,14 @@ 

Functions

-def print_table(title, data, type_='ascii') +def print_table(title, data, type_='ascii')
-
+
-Source code + +Expand source code +
def print_table(title, data, type_="ascii"):
     print(create(title, data, type_))
@@ -150,9 +158,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/text/index.html b/docs/api/jumpscale/data/text/index.html index 20cfaab08..e278e3f4f 100644 --- a/docs/api/jumpscale/data/text/index.html +++ b/docs/api/jumpscale/data/text/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.text API documentation - - - - - + + + + + + +
@@ -22,28 +24,30 @@

Module jumpscale.data.text

helpers around string manipulation.

Remove prefix

-
JS-NG> j.data.text.removeprefix("ahhmed", "ah")                                                                                                                                          
+
JS-NG> j.data.text.removeprefix("ahhmed", "ah")
 'hmed'
 

Remove suffix

-
JS-NG> j.data.text.removesuffix("ahhmed.3bot", ".3bot")                                                                                                                                  
+
JS-NG> j.data.text.removesuffix("ahhmed.3bot", ".3bot")
 'ahhmed'
 
-Source code + +Expand source code +
"""helpers around string manipulation.
 
 
-## Remove prefix 
+## Remove prefix
 ```
-JS-NG> j.data.text.removeprefix("ahhmed", "ah")                                                                                                                                          
+JS-NG> j.data.text.removeprefix("ahhmed", "ah")
 'hmed'
 ```
 
 ## Remove suffix
 
 ```
-JS-NG> j.data.text.removesuffix("ahhmed.3bot", ".3bot")                                                                                                                                  
+JS-NG> j.data.text.removesuffix("ahhmed.3bot", ".3bot")
 'ahhmed'
 ```
 """
@@ -90,10 +94,10 @@ 

Remove suffix

Functions

-def removeprefix(s, prefix) +def removeprefix(s: str, prefix: str) ‑> str
-

Remove a prefix string prefix from a string s.

+

Remove a prefix string prefix from a string s.

Args

s : str
@@ -103,11 +107,13 @@

Args

Returns

-
str
+
str
string without the prefix part
-
+
-Source code + +Expand source code +
def removeprefix(s: str, prefix: str) -> str:
     """Remove a prefix string `prefix` from a string `s`.
 
@@ -126,10 +132,10 @@ 

Returns

-def removesuffix(s, suffix) +def removesuffix(s: str, suffix: str) ‑> str
-

Remove a suffix string suffix from a string s.

+

Remove a suffix string suffix from a string s.

Args

s : str
@@ -139,11 +145,13 @@

Args

Returns

-
str
+
str
string without the suffix part
-
+
-Source code + +Expand source code +
def removesuffix(s: str, suffix: str) -> str:
     """Remove a suffix string `suffix` from a string `s`.
 
@@ -190,9 +198,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/time/index.html b/docs/api/jumpscale/data/time/index.html index 01e1c8cfc..d72f02b30 100644 --- a/docs/api/jumpscale/data/time/index.html +++ b/docs/api/jumpscale/data/time/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.time API documentation - - - - - + + + + + + +
@@ -22,7 +24,7 @@

Module jumpscale.data.time

Time helpers based on arrow

TODO: add more explanation here.

-
>>> j.data.time.get('2013-05-11T21:23:58.970460+07:00')
+
>>> j.data.time.get('2013-05-11T21:23:58.970460+07:00')
 <Arrow [2013-05-11T21:23:58.970460+07:00]>
 
 >>> utc = j.data.time.utcnow()
@@ -33,89 +35,82 @@ 

TODO: add more explanation here.

>>> utc <Arrow [2013-05-11T20:23:58.970460+00:00]> ->>> j.data.time.now() +>>> j.data.time.now() <Arrow [2020-04-09T10:19:19.013636+02:00]> ->>> j.data.time.now().shift(hours=15) +>>> j.data.time.now().shift(hours=15) <Arrow [2020-04-10T01:19:23.225311+02:00]> +>>> local = utc.to('US/Pacific') +>>> local +<Arrow [2013-05-11T13:23:58.970460-07:00]> - >>> local = utc.to('US/Pacific') - >>> local - <Arrow [2013-05-11T13:23:58.970460-07:00]> - - >>> local.timestamp - 1368303838 - - >>> local.format() - '2013-05-11 13:23:58 -07:00' - - >>> local.format('YYYY-MM-DD HH:mm:ss ZZ') - '2013-05-11 13:23:58 -07:00' - - >>> local.humanize() - 'an hour ago' - - >>> local.humanize(locale='ko_kr') - '1시간 전' - - - - +>>> local.timestamp +1368303838 - >>> j.data.time.utcnow() - <Arrow [2013-05-07T04:20:39.369271+00:00]> +>>> local.format() +'2013-05-11 13:23:58 -07:00' - >>> j.data.time.now() - <Arrow [2013-05-06T21:20:40.841085-07:00]> +>>> local.format('YYYY-MM-DD HH:mm:ss ZZ') +'2013-05-11 13:23:58 -07:00' - >>> j.data.time.now('US/Pacific') - <Arrow [2013-05-06T21:20:44.761511-07:00]> +>>> local.humanize() +'an hour ago' - >>> j.data.time.get(1367900664) - <Arrow [2013-05-07T04:24:24+00:00]> +>>> local.humanize(locale='ko_kr') +'1시간 전' - >>> j.data.time.get(1367900664.152325) - <Arrow [2013-05-07T04:24:24.152325+00:00]> - >>> j.data.time.get(datetime.utcnow()) - <Arrow [2013-05-07T04:24:24.152325+00:00]> +>>> j.data.time.utcnow() +<Arrow [2013-05-07T04:20:39.369271+00:00]> - >>> j.data.time.get(datetime(2013, 5, 5), 'US/Pacific') - <Arrow [2013-05-05T00:00:00-07:00]> +>>> j.data.time.now() +<Arrow [2013-05-06T21:20:40.841085-07:00]> - >>> from dateutil import tz - >>> j.data.time.get(datetime(2013, 5, 5), tz.gettz('US/Pacific')) - <Arrow [2013-05-05T00:00:00-07:00]> +>>> j.data.time.now('US/Pacific') +<Arrow [2013-05-06T21:20:44.761511-07:00]> - >>> j.data.time.get(datetime.now(tz.gettz('US/Pacific'))) - <Arrow [2013-05-06T21:24:49.552236-07:00]> +>>> j.data.time.get(1367900664) +<Arrow [2013-05-07T04:24:24+00:00]> - >>> j.data.time.get('2013-05-05 12:30:45', 'YYYY-MM-DD HH:mm:ss') - <Arrow [2013-05-05T12:30:45+00:00]> +>>> j.data.time.get(1367900664.152325) +<Arrow [2013-05-07T04:24:24.152325+00:00]> - >>> j.data.time.get('June was born in May 1980', 'MMMM YYYY') - <Arrow [1980-05-01T00:00:00+00:00]> +>>> j.data.time.get(datetime.utcnow()) +<Arrow [2013-05-07T04:24:24.152325+00:00]> +>>> j.data.time.get(datetime(2013, 5, 5), 'US/Pacific') +<Arrow [2013-05-05T00:00:00-07:00]> +>>> from dateutil import tz +>>> j.data.time.get(datetime(2013, 5, 5), tz.gettz('US/Pacific')) +<Arrow [2013-05-05T00:00:00-07:00]> +>>> j.data.time.get(datetime.now(tz.gettz('US/Pacific'))) +<Arrow [2013-05-06T21:24:49.552236-07:00]> +>>> j.data.time.get('2013-05-05 12:30:45', 'YYYY-MM-DD HH:mm:ss') +<Arrow [2013-05-05T12:30:45+00:00]> - >>> arw = j.data.time.utcnow() - >>> arw - <Arrow [2013-05-12T03:29:35.334214+00:00]> +>>> j.data.time.get('June was born in May 1980', 'MMMM YYYY') +<Arrow [1980-05-01T00:00:00+00:00]> - >>> arw.replace(hour=4, minute=40) - <Arrow [2013-05-12T04:40:35.334214+00:00]> - >>> arw.shift(weeks=+3) - <Arrow [2013-06-02T03:29:35.334214+00:00]> +>>> arw = j.data.time.utcnow() +>>> arw +<Arrow [2013-05-12T03:29:35.334214+00:00]> +>>> arw.replace(hour=4, minute=40) +<Arrow [2013-05-12T04:40:35.334214+00:00]> +>>> arw.shift(weeks=+3) +<Arrow [2013-06-02T03:29:35.334214+00:00]>
-Source code + +Expand source code +
"""
 Time helpers based on arrow
 
@@ -133,10 +128,10 @@ 

TODO: add more explanation here.

>>> utc <Arrow [2013-05-11T20:23:58.970460+00:00]> ->>> j.data.time.now() +>>> j.data.time.now() <Arrow [2020-04-09T10:19:19.013636+02:00]> ->>> j.data.time.now().shift(hours=15) +>>> j.data.time.now().shift(hours=15) <Arrow [2020-04-10T01:19:23.225311+02:00]> @@ -237,9 +232,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/types/index.html b/docs/api/jumpscale/data/types/index.html index 6f62e773b..5b640776d 100644 --- a/docs/api/jumpscale/data/types/index.html +++ b/docs/api/jumpscale/data/types/index.html @@ -3,15 +3,17 @@ - + jumpscale.data.types API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.types

-Source code + +Expand source code +
from .types import *
@@ -30,11 +34,11 @@

Sub-modules

jumpscale.data.types.pritypes
-
+
jumpscale.data.types.types
-
+
@@ -66,9 +70,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/types/pritypes.html b/docs/api/jumpscale/data/types/pritypes.html index c9dfa0051..716122332 100644 --- a/docs/api/jumpscale/data/types/pritypes.html +++ b/docs/api/jumpscale/data/types/pritypes.html @@ -3,15 +3,17 @@ - + jumpscale.data.types.pritypes API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.types.pritypes

-Source code + +Expand source code +
import ast
 
 
@@ -48,7 +52,7 @@ 

Module jumpscale.data.types.pritypes

def check(self, value): """Check whether provided string represent integer value - + Arguments: value (str) """ @@ -60,7 +64,7 @@

Module jumpscale.data.types.pritypes

def from_str(self, value): """get integer value from tha string - + Arguments: value (str) """ @@ -107,7 +111,7 @@

Module jumpscale.data.types.pritypes

def check(self, value): """Check whether provided string represent integer value - + Arguments: value (str) """ @@ -119,7 +123,7 @@

Module jumpscale.data.types.pritypes

def from_str(self, value): """get integer value from tha string - + Arguments: value (str) """ @@ -136,7 +140,7 @@

Module jumpscale.data.types.pritypes

def check(self, value): """Check whether provided string represent JSObject value. (Any string will do). - + Arguments: value (str) """ @@ -144,7 +148,7 @@

Module jumpscale.data.types.pritypes

def from_str(self, value): """Return value as is. - + Arguments: value (str) """ @@ -159,10 +163,10 @@

Module jumpscale.data.types.pritypes

def _deep_check(self, value): """Check that the value represents a list with proper elements of the specified subtype. - + Args: value (list): The list to be checked. - + Returns: Boolean: True if the list is valid. """ @@ -177,10 +181,10 @@

Module jumpscale.data.types.pritypes

def check(self, value): """Check that the value represents a list with proper elements of the specified subtype. - + Args: value (list): The list to be checked. - + Returns: Boolean: True if the list is valid. """ @@ -195,10 +199,10 @@

Module jumpscale.data.types.pritypes

def _deep_parse(self, value): """parses the subelements (if they are of different python type it's converted using the subtype parser) - + Args: value (list): The list to be parsed. - + Returns: list: The parsed list. """ @@ -213,10 +217,10 @@

Module jumpscale.data.types.pritypes

def from_str(self, value): """parses the string value into a list. - + Args: value (str): The string to be parsed. - + Returns: list: The parsed list. """ @@ -236,11 +240,11 @@

Module jumpscale.data.types.pritypes

4. "F" -> Float 5. "L.*" -> List with subtype .* 6. "" -> empty defaults to String - + Args: type_str (str): type description. default_value (any, optional): The default value. Defaults to None. - + Returns: Object: A js type object. """ @@ -263,7 +267,7 @@

Functions

def get_js_type(type_str, default_value=None)
-

Gets the js type from a string.

+

Gets the js type from a string.

  1. "S" -> String
  2. "B" -> Boolean
  3. @@ -282,11 +286,13 @@

    Args

    Returns

    -
    Object
    +
    Object
    A js type object.
    -
+
-Source code + +Expand source code +
def get_js_type(type_str, default_value=None):
     """Gets the js type from a string.
 
@@ -297,11 +303,11 @@ 

Returns

4. "F" -> Float 5. "L.*" -> List with subtype .* 6. "" -> empty defaults to String - + Args: type_str (str): type description. default_value (any, optional): The default value. Defaults to None. - + Returns: Object: A js type object. """ @@ -323,9 +329,11 @@

Classes

(default=None)
-
+
-Source code + +Expand source code +
class Boolean:
     def __init__(self, default=None):
         if not default:
@@ -360,11 +368,13 @@ 

Methods

def check(self, value)
-

Check whether provided string represent Boolean expresion

+

Check whether provided string represent Boolean expresion

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided string represent Boolean expresion
 
@@ -379,11 +389,13 @@ 

Arguments

def from_str(self, value)
-

Get the Boolean value from the string

+

Get the Boolean value from the string

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """Get the Boolean value from the string
 
@@ -405,9 +417,11 @@ 

Arguments

(default=None)
-
+
-Source code + +Expand source code +
class Float:
     def __init__(self, default=None):
         if not default:
@@ -416,7 +430,7 @@ 

Arguments

def check(self, value): """Check whether provided string represent integer value - + Arguments: value (str) """ @@ -428,7 +442,7 @@

Arguments

def from_str(self, value): """get integer value from tha string - + Arguments: value (str) """ @@ -443,14 +457,16 @@

Methods

def check(self, value)
-

Check whether provided string represent integer value

+

Check whether provided string represent integer value

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided string represent integer value
-    
+
     Arguments:
         value (str)
     """
@@ -465,14 +481,16 @@ 

Arguments

def from_str(self, value)
-

get integer value from tha string

+

get integer value from tha string

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get integer value from tha string
-    
+
     Arguments:
         value (str)
     """
@@ -489,9 +507,11 @@ 

Arguments

(default=None)
-
+
-Source code + +Expand source code +
class Integer:
     def __init__(self, default=None):
         if not default:
@@ -500,7 +520,7 @@ 

Arguments

def check(self, value): """Check whether provided string represent integer value - + Arguments: value (str) """ @@ -512,7 +532,7 @@

Arguments

def from_str(self, value): """get integer value from tha string - + Arguments: value (str) """ @@ -527,14 +547,16 @@

Methods

def check(self, value)
-

Check whether provided string represent integer value

+

Check whether provided string represent integer value

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided string represent integer value
-    
+
     Arguments:
         value (str)
     """
@@ -549,14 +571,16 @@ 

Arguments

def from_str(self, value)
-

get integer value from tha string

+

get integer value from tha string

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get integer value from tha string
-    
+
     Arguments:
         value (str)
     """
@@ -573,9 +597,11 @@ 

Arguments

(default=None)
-
+
-Source code + +Expand source code +
class JSObject:
     def __init__(self, default=None):
         default = default or ""
@@ -583,7 +609,7 @@ 

Arguments

def check(self, value): """Check whether provided string represent JSObject value. (Any string will do). - + Arguments: value (str) """ @@ -591,7 +617,7 @@

Arguments

def from_str(self, value): """Return value as is. - + Arguments: value (str) """ @@ -603,14 +629,16 @@

Methods

def check(self, value)
-

Check whether provided string represent JSObject value. (Any string will do).

+

Check whether provided string represent JSObject value. (Any string will do).

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided string represent JSObject value. (Any string will do).
-    
+
     Arguments:
         value (str)
     """
@@ -621,14 +649,16 @@ 

Arguments

def from_str(self, value)
-

Return value as is.

+

Return value as is.

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """Return value as is.
-    
+
     Arguments:
         value (str)
     """
@@ -642,9 +672,11 @@ 

Arguments

(default, subtype)
-
+
-Source code + +Expand source code +
class List:
     def __init__(self, default, subtype):
         self.subtype = subtype
@@ -653,10 +685,10 @@ 

Arguments

def _deep_check(self, value): """Check that the value represents a list with proper elements of the specified subtype. - + Args: value (list): The list to be checked. - + Returns: Boolean: True if the list is valid. """ @@ -671,10 +703,10 @@

Arguments

def check(self, value): """Check that the value represents a list with proper elements of the specified subtype. - + Args: value (list): The list to be checked. - + Returns: Boolean: True if the list is valid. """ @@ -689,10 +721,10 @@

Arguments

def _deep_parse(self, value): """parses the subelements (if they are of different python type it's converted using the subtype parser) - + Args: value (list): The list to be parsed. - + Returns: list: The parsed list. """ @@ -707,10 +739,10 @@

Arguments

def from_str(self, value): """parses the string value into a list. - + Args: value (str): The string to be parsed. - + Returns: list: The parsed list. """ @@ -725,7 +757,7 @@

Methods

def check(self, value)
-

Check that the value represents a list with proper elements of the specified subtype.

+

Check that the value represents a list with proper elements of the specified subtype.

Args

value : list
@@ -733,17 +765,19 @@

Args

Returns

-
Boolean
+
Boolean
True if the list is valid.
-
+
-Source code + +Expand source code +
def check(self, value):
     """Check that the value represents a list with proper elements of the specified subtype.
-    
+
     Args:
         value (list): The list to be checked.
-    
+
     Returns:
         Boolean: True if the list is valid.
     """
@@ -761,7 +795,7 @@ 

Returns

def from_str(self, value)
-

parses the string value into a list.

+

parses the string value into a list.

Args

value : str
@@ -769,17 +803,19 @@

Args

Returns

-
list
+
list
The parsed list.
-
+
-Source code + +Expand source code +
def from_str(self, value):
     """parses the string value into a list.
-    
+
     Args:
         value (str): The string to be parsed.
-    
+
     Returns:
         list: The parsed list.
     """
@@ -796,9 +832,11 @@ 

Returns

(default=None)
-
+
-Source code + +Expand source code +
class String:
     def __init__(self, default=None):
         if not default:
@@ -819,9 +857,11 @@ 

Methods

def check(self, value)
-

Check whether provided value is a string

+

Check whether provided value is a string

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a string"""
     return isinstance(value, str)
@@ -831,9 +871,11 @@

Methods

def from_str(self, value)
-

return string from a string (is basically no more than a check)

+

return string from a string (is basically no more than a check)

-Source code + +Expand source code +
def from_str(self, value):
     """return string from a string (is basically no more than a check)"""
     return value
@@ -910,9 +952,7 @@

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/data/types/types.html b/docs/api/jumpscale/data/types/types.html index f089f5f5a..50cc410cc 100644 --- a/docs/api/jumpscale/data/types/types.html +++ b/docs/api/jumpscale/data/types/types.html @@ -3,15 +3,17 @@ - + jumpscale.data.types.types API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.data.types.types

-Source code + +Expand source code +
import ast
 import re
 
@@ -517,7 +521,7 @@ 

Functions

def get_js_type(type_str, default_value=None)
-

Gets the js type from a string.

+

Gets the js type from a string.

  1. "S" -> String
  2. "B" -> Boolean
  3. @@ -537,11 +541,13 @@

    Args

    Returns

    -
    Object
    +
    Object
    A js type object.
    -
+
-Source code + +Expand source code +
def get_js_type(type_str, default_value=None):
     """Gets the js type from a string.
 
@@ -600,9 +606,11 @@ 

Classes

(default=None)
-
+
-Source code + +Expand source code +
class Boolean:
     def __init__(self, default=None):
         if not default:
@@ -637,11 +645,13 @@ 

Methods

def check(self, value)
-

Check whether provided string represent Boolean expresion

+

Check whether provided string represent Boolean expresion

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided string represent Boolean expresion
 
@@ -656,11 +666,13 @@ 

Arguments

def from_str(self, value)
-

Get the Boolean value from the string

+

Get the Boolean value from the string

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """Get the Boolean value from the string
 
@@ -682,9 +694,11 @@ 

Arguments

(default=None)
-

Support yyyy-mm-dd format

+

Support yyyy-mm-dd format

-Source code + +Expand source code +
class Date:
     """Support yyyy-mm-dd format"""
 
@@ -716,13 +730,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid date representation

+

Check whether provided value is a valid date representation

Arguments

value (str)

Returbs

-

Boolean expresion

+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid date representation
     Arguments:
@@ -736,13 +752,15 @@ 

Returbs

def from_str(self, value)
-

get the value from the string

+

get the value from the string

Arguments

value (str)

Returbs

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get the value from the string
     Arguments:
@@ -759,9 +777,11 @@ 

Returbs

(default=None)
-

Supported format: yyyy-mm-dd hh:mm

+

Supported format: yyyy-mm-dd hh:mm

-Source code + +Expand source code +
class DateTime:
     """Supported format: yyyy-mm-dd hh:mm"""
 
@@ -793,13 +813,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid datetime representation

+

Check whether provided value is a valid datetime representation

Arguments

value (str)

Returbs

-

Boolean expresion

+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid datetime representation
     Arguments:
@@ -813,13 +835,15 @@ 

Returbs

def from_str(self, value)
-

get the value from the string

+

get the value from the string

Arguments

value (str)

Returbs

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get the value from the string
     Arguments:
@@ -836,9 +860,11 @@ 

Returbs

(default=None)
-

Supported format : (n)y (n)m (n)d (n)h (n)m (n)s

+

Supported format : (n)y (n)m (n)d (n)h (n)m (n)s

-Source code + +Expand source code +
class Duration:
     """Supported format : (n)y (n)m (n)d (n)h (n)m (n)s"""
 
@@ -869,13 +895,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid duration representation

+

Check whether provided value is a valid duration representation

Arguments

value (str)

Returbs

-

Boolean expresion

+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid duration representation
     Arguments:
@@ -889,12 +917,14 @@ 

Returbs

def from_str(self, value)
-

Get the value from string +

Get the value from string value (str)

Returbs

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """Get the value from string
         value (str)
@@ -910,9 +940,11 @@ 

Returbs

(default=None)
-
+
-Source code + +Expand source code +
class Email:
     def __init__(self, default=None):
         if not default:
@@ -942,13 +974,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid email representation

+

Check whether provided value is a valid email representation

Arguments

value (str)

Returbs

-

Boolean expresion

+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid email representation
     Arguments:
@@ -962,13 +996,15 @@ 

Returbs

def from_str(self, value)
-

get the value from the string

+

get the value from the string

Arguments

value (str)

Returbs

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get the value from the string
     Arguments:
@@ -985,9 +1021,11 @@ 

Returbs

(default=None)
-
+
-Source code + +Expand source code +
class Float:
     def __init__(self, default=None):
         if not default:
@@ -1023,11 +1061,13 @@ 

Methods

def check(self, value)
-

Check whether provided string represent integer value

+

Check whether provided string represent integer value

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided string represent integer value
 
@@ -1045,11 +1085,13 @@ 

Arguments

def from_str(self, value)
-

get integer value from tha string

+

get integer value from tha string

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get integer value from tha string
 
@@ -1069,9 +1111,11 @@ 

Arguments

(default=None)
-
+
-Source code + +Expand source code +
class IPAddress:
     def __init__(self, default=None):
         if not default:
@@ -1106,16 +1150,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid IPaddress representation

+

Check whether provided value is a valid IPaddress representation

Arguments

value (str)

Returns

-
-
Boolean expresion
-
 
-
+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid IPaddress representation
     Arguments:
@@ -1135,16 +1178,15 @@ 

Returns

def from_str(self, value)
-

get the value from the string

+

get the value from the string

Arguments

value (str)

Returns

-
-
value (str)
-
 
-
+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get the value from the string
     Arguments:
@@ -1161,9 +1203,11 @@ 

Returns

(default=None)
-
+
-Source code + +Expand source code +
class Integer:
     def __init__(self, default=None):
         if not default:
@@ -1199,11 +1243,13 @@ 

Methods

def check(self, value)
-

Check whether provided string represent integer value

+

Check whether provided string represent integer value

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided string represent integer value
 
@@ -1221,11 +1267,13 @@ 

Arguments

def from_str(self, value)
-

get integer value from tha string

+

get integer value from tha string

Arguments

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get integer value from tha string
 
@@ -1245,9 +1293,11 @@ 

Arguments

(default=None)
-
+
-Source code + +Expand source code +
class JSObject:
     def __init__(self, default=None):
         default = default or ""
@@ -1265,9 +1315,11 @@ 

Methods

def check(self, value)
-
+
-Source code + +Expand source code +
def check(self, value):
     return True
@@ -1276,9 +1328,11 @@

Methods

def from_str(self, value)
-
+
-Source code + +Expand source code +
def from_str(self, value):
     return value
@@ -1290,9 +1344,11 @@

Methods

(default, subtype)
-
+
-Source code + +Expand source code +
class List:
     def __init__(self, default, subtype):
         self.subtype = subtype
@@ -1364,7 +1420,12 @@ 

Methods

""" # print(f"from str-> value {value}") # print(f"subtype {self.subtype}") - return object()
+ return object() + # if isinstance(self.subtype, JSObject): + # print("returning noww") + # return object() + # else: + # return self._deep_parse(ast.literal_eval(value))

Methods

@@ -1372,7 +1433,7 @@

Methods

def check(self, value)
-

Check that the value represents a list with proper elements of the specified subtype.

+

Check that the value represents a list with proper elements of the specified subtype.

Args

value : list
@@ -1380,11 +1441,13 @@

Args

Returns

-
Boolean
+
Boolean
True if the list is valid.
-
+
-Source code + +Expand source code +
def check(self, value):
     """Check that the value represents a list with proper elements of the specified subtype.
 
@@ -1408,7 +1471,7 @@ 

Returns

def from_str(self, value)
-

parses the string value into a list.

+

parses the string value into a list.

Args

value : str
@@ -1416,11 +1479,13 @@

Args

Returns

-
list
+
list
The parsed list.
-
+
-Source code + +Expand source code +
def from_str(self, value):
     """parses the string value into a list.
 
@@ -1432,7 +1497,12 @@ 

Returns

""" # print(f"from str-> value {value}") # print(f"subtype {self.subtype}") - return object()
+ return object() + # if isinstance(self.subtype, JSObject): + # print("returning noww") + # return object() + # else: + # return self._deep_parse(ast.literal_eval(value))
@@ -1442,9 +1512,11 @@

Returns

(default=None)
-
+
-Source code + +Expand source code +
class Path:
     def __init__(self, default=None):
         if not default:
@@ -1474,13 +1546,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid path representation

+

Check whether provided value is a valid path representation

Arguments

value (str)

Returbs

-

Boolean expresion

+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid path representation
     Arguments:
@@ -1494,13 +1568,15 @@ 

Returbs

def from_str(self, value)
-

get the value from the string

+

get the value from the string

Arguments

value (str)

Returbs

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get the value from the string
     Arguments:
@@ -1517,9 +1593,11 @@ 

Returbs

(default=None)
-
+
-Source code + +Expand source code +
class String:
     def __init__(self, default=None):
         if not default:
@@ -1540,9 +1618,11 @@ 

Methods

def check(self, value)
-

Check whether provided value is a string

+

Check whether provided value is a string

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a string"""
     return isinstance(value, str)
@@ -1552,9 +1632,11 @@

Methods

def from_str(self, value)
-

return string from a string (is basically no more than a check)

+

return string from a string (is basically no more than a check)

-Source code + +Expand source code +
def from_str(self, value):
     """return string from a string (is basically no more than a check)"""
     return value
@@ -1567,9 +1649,11 @@

Methods

(default=None)
-
+
-Source code + +Expand source code +
class Tel:
     def __init__(self, default=None):
         if not default:
@@ -1609,13 +1693,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid Telephone number representation

+

Check whether provided value is a valid Telephone number representation

Arguments

value (str)

Returbs

-

Boolean expresion

+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid Telephone number representation
     Arguments:
@@ -1630,13 +1716,15 @@ 

Returbs

def from_str(self, value)
-

get the value from the string

+

get the value from the string

Arguments

value (str)

Returbs

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get the value from the string
     Arguments:
@@ -1653,9 +1741,11 @@ 

Returbs

(default=None)
-

Supported format : hh:mm

+

Supported format : hh:mm

-Source code + +Expand source code +
class Time:
     """Supported format : hh:mm"""
 
@@ -1686,13 +1776,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid time representation

+

Check whether provided value is a valid time representation

Arguments

value (str)

Returbs

-

Boolean expresion

+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid time representation
     Arguments:
@@ -1706,12 +1798,14 @@ 

Returbs

def from_str(self, value)
-

Get the value from string +

Get the value from string value (str)

Returbs

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """Get the value from string
         value (str)
@@ -1727,9 +1821,11 @@ 

Returbs

(default=None)
-
+
-Source code + +Expand source code +
class URL:
     def __init__(self, default=None):
         if not default:
@@ -1759,13 +1855,15 @@ 

Methods

def check(self, value)
-

Check whether provided value is a valid URL representation

+

Check whether provided value is a valid URL representation

Arguments

value (str)

Returbs

-

Boolean expresion

+

Boolean expresion

-Source code + +Expand source code +
def check(self, value):
     """Check whether provided value is a valid URL representation
     Arguments:
@@ -1779,13 +1877,15 @@ 

Returbs

def from_str(self, value)
-

get the value from the string

+

get the value from the string

Arguments

value (str)

Returbs

-

value (str)

+

value (str)

-Source code + +Expand source code +
def from_str(self, value):
     """get the value from the string
     Arguments:
@@ -1929,9 +2029,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/entry_points/index.html b/docs/api/jumpscale/entry_points/index.html index ba9395744..17a7e0e7e 100644 --- a/docs/api/jumpscale/entry_points/index.html +++ b/docs/api/jumpscale/entry_points/index.html @@ -3,15 +3,17 @@ - + jumpscale.entry_points API documentation - - - - - + + + + + + +
@@ -26,22 +28,22 @@

Sub-modules

jumpscale.entry_points.jsctl
-
+
jumpscale.entry_points.jsng
-
+
jumpscale.entry_points.jsync
-

jumpscale.entry_points.jsync is syncing tool to sync over certain set of directories against remote machine. +

jumpscale.entry_points.jsync is syncing tool to sync over certain set of directories against remote machine. list available clients ``` -~> poetry run jsync …

+~> poetry run jsync …

jumpscale.entry_points.usershell
-
+

@@ -75,9 +77,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/entry_points/jsctl.html b/docs/api/jumpscale/entry_points/jsctl.html index 1102955a1..75286403a 100644 --- a/docs/api/jumpscale/entry_points/jsctl.html +++ b/docs/api/jumpscale/entry_points/jsctl.html @@ -3,15 +3,17 @@ - + jumpscale.entry_points.jsctl API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.entry_points.jsctl

-Source code + +Expand source code +
import click
 
 from jumpscale.core.config import get_default_config, update_config, get_config
@@ -149,9 +153,11 @@ 

Functions

def decode_toml_value(s)
-
+
-Source code + +Expand source code +
def decode_toml_value(s):
     try:
         return toml.TomlDecoder().load_value(s)[0]
@@ -163,9 +169,11 @@ 

Functions

def encode_toml_value(s)
-
+
-Source code + +Expand source code +
def encode_toml_value(s):
     return toml.TomlEncoder().dump_value(s)
@@ -174,9 +182,11 @@

Functions

def format_config(config)
-
+
-Source code + +Expand source code +
def format_config(config):
     print_dict(config, "")
@@ -185,9 +195,11 @@

Functions

def format_config_parameter(name, value)
-
+
-Source code + +Expand source code +
def format_config_parameter(name, value):
     if isinstance(value, dict):
         print_dict(value, f"{name}.")
@@ -199,9 +211,11 @@ 

Functions

def print_dict(data, path)
-
+
-Source code + +Expand source code +
def print_dict(data, path):
     for name, value in data.items():
         if isinstance(value, dict):
@@ -215,9 +229,11 @@ 

Functions

def traverse_config(name)
-
+
-Source code + +Expand source code +
def traverse_config(name):
     path = name.split(".")
     config = get_config()
@@ -231,9 +247,11 @@ 

Functions

def validate_type(a, b)
-
+
-Source code + +Expand source code +
def validate_type(a, b):
     typea = type(a).__name__
     typeb = type(b).__name__
@@ -272,9 +290,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/entry_points/jsng.html b/docs/api/jumpscale/entry_points/jsng.html index ccf569bf5..62e1cf752 100644 --- a/docs/api/jumpscale/entry_points/jsng.html +++ b/docs/api/jumpscale/entry_points/jsng.html @@ -3,15 +3,17 @@ - + jumpscale.entry_points.jsng API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.entry_points.jsng

-Source code + +Expand source code +
from gevent import monkey
 
 monkey.patch_all(subprocess=False)  # noqa: E402
@@ -84,9 +88,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/entry_points/jsync.html b/docs/api/jumpscale/entry_points/jsync.html index e5baaf4e5..4a004024e 100644 --- a/docs/api/jumpscale/entry_points/jsync.html +++ b/docs/api/jumpscale/entry_points/jsync.html @@ -3,18 +3,20 @@ - + jumpscale.entry_points.jsync API documentation - - - - - + + + + + + +
@@ -33,7 +35,9 @@

sync with a client certain set ['xmonader'] {'~/wspace/tq': '~/wspace/tq', '/tmp/proj': '/tmp/proj2'}

-Source code + +Expand source code +
"""`jsync` is syncing tool to sync over certain set of directories against remote machine.
 ## list available clients
 ```
@@ -119,9 +123,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/entry_points/usershell.html b/docs/api/jumpscale/entry_points/usershell.html index bbd226de1..767d0895f 100644 --- a/docs/api/jumpscale/entry_points/usershell.html +++ b/docs/api/jumpscale/entry_points/usershell.html @@ -3,15 +3,17 @@ - + jumpscale.entry_points.usershell API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.entry_points.usershell

-Source code + +Expand source code +
import os
 import re
 import time
@@ -282,9 +286,11 @@ 

Functions

def get_binary_link()
-
+
-Source code + +Expand source code +
def get_binary_link():
     resp = requests.get("https://api.github.com/repos/threefoldtech/js-ng/releases/latest")
     resp = resp.json()
@@ -301,9 +307,11 @@ 

Functions

def noexpert_error(error)
-
+
-Source code + +Expand source code +
def noexpert_error(error):
     reports_location = f"{os.environ.get('HOME', os.environ.get('USERPROFILE', ''))}/sandbox/reports"
     error_file_location = f"{reports_location}/jsngreport_{time.strftime('%d%H%M%S')}.log"
@@ -321,9 +329,11 @@ 

Functions

def partition_line(line)
-
+
-Source code + +Expand source code +
def partition_line(line):
     def replacer(m):
         return m.group().replace(" ", "\0").strip("\"'")
@@ -339,9 +349,11 @@ 

Functions

def print_error(error)
-
+
-Source code + +Expand source code +
def print_error(error):
     print_formatted_text(HTML("<ansired>{}</ansired>".format(cgi.html.escape(str(error)))))
@@ -350,9 +362,11 @@

Functions

def run()
-
+
-Source code + +Expand source code +
def run():
     parser = argparse.ArgumentParser()
     parser.add_argument("--update", action="store_true", help="Update 3sdk")
@@ -371,9 +385,11 @@ 

Functions

def update()
-
+
-Source code + +Expand source code +
def update():
     print("checking for updates")
     latest_version, binary_link = get_binary_link()
@@ -399,16 +415,18 @@ 

Classes

class Shell
-

Abstract base class for an input validator.

+

Abstract base class for an input validator.

A validator is typically created in one of the following two ways:

  • Either by overriding this class and implementing the validate method.
  • Or by passing a callable to Validator.from_callable.

If the validation takes some time and needs to happen in a background -thread, this can be wrapped in a :class:.ThreadedValidator.

+thread, this can be wrapped in a :class:.ThreadedValidator.

-Source code + +Expand source code +
class Shell(Validator):
     def __init__(self):
         self._prompt = PromptSession()
@@ -552,9 +570,11 @@ 

Methods

def bottom_toolbar(self)
-
+
-Source code + +Expand source code +
def bottom_toolbar(self):
     return [("class:bottom-toolbar", self.toolbarmsg)]
@@ -563,9 +583,11 @@

Methods

def execute(self, cmd)
-
+
-Source code + +Expand source code +
def execute(self, cmd):
     if not cmd.strip():
         return
@@ -588,9 +610,11 @@ 

Methods

def get_completions_async(self, document, complete_event)
-
+
-Source code + +Expand source code +
def get_completions_async(self, document, complete_event):
     text = document.current_line_before_cursor
     parts = partition_line(text)
@@ -646,9 +670,11 @@ 

Methods

def get_func_kwargs(self, cmd)
-
+
-Source code + +Expand source code +
def get_func_kwargs(self, cmd):
     parts = partition_line(cmd)
     root, extra = parts[0], parts[1:]
@@ -664,9 +690,11 @@ 

Methods

def get_kwargs(self, func, *args)
-
+
-Source code + +Expand source code +
def get_kwargs(self, func, *args):
     funcspec = inspect.getfullargspec(func)
     kwargs = {}
@@ -686,9 +714,11 @@ 

Methods

def make_prompt(self)
-
+
-Source code + +Expand source code +
def make_prompt(self):
     root = ("class:default", "3sdk>")
     while True:
@@ -703,9 +733,11 @@ 

Methods

def prompt(self, msg)
-
+
-Source code + +Expand source code +
def prompt(self, msg):
     return self._prompt.prompt(
         msg, completer=self, validator=self, style=style, bottom_toolbar=self.bottom_toolbar,
@@ -716,11 +748,13 @@ 

Methods

def validate(self, document)
-

Validate the input. +

Validate the input. If invalid, this should raise a :class:.ValidationError.

-

:param document: :class:~prompt_toolkit.document.Document instance.

+

:param document: :class:~prompt_toolkit.document.Document instance.

-Source code + +Expand source code +
def validate(self, document):
     text = document.current_line_before_cursor
     if not text:
@@ -788,9 +822,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/index.html b/docs/api/jumpscale/index.html index 8cf46f502..bbf6a37c3 100644 --- a/docs/api/jumpscale/index.html +++ b/docs/api/jumpscale/index.html @@ -3,15 +3,17 @@ - + jumpscale API documentation - - - - - + + + + + + +
@@ -26,43 +28,43 @@

Sub-modules

jumpscale.clients
-
+
jumpscale.core
-
+
jumpscale.data
-
+
jumpscale.entry_points
-
+
jumpscale.loader
-

This module exposes a j object, which contains a reference to all sub namespaces and modules available under jumpscale …

+

This module exposes a j object, which contains a reference to all sub namespaces and modules available under jumpscale …

jumpscale.sals
-
+
jumpscale.servers
-
+
jumpscale.shell
-

This module defines all of js-ng specific shell features …

+

This module defines all of js-ng specific shell features …

jumpscale.threesdk
-
+
jumpscale.tools
-
+

@@ -97,9 +99,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/loader.html b/docs/api/jumpscale/loader.html index 5e2744dfa..3c7cbaed6 100644 --- a/docs/api/jumpscale/loader.html +++ b/docs/api/jumpscale/loader.html @@ -3,15 +3,17 @@ - + jumpscale.loader API documentation - - - - - + + + + + + +
@@ -24,7 +26,9 @@

Module jumpscale.loader

It creates a container classes/types dynamically, inject attributes into these classes with references to sub namespaces and modules, and at the end, create an instance from this container class.

-Source code + +Expand source code +
"""
 This module exposes a j object, which contains a reference to all sub namespaces and modules available under jumpscale
 
@@ -138,19 +142,21 @@ 

Module jumpscale.loader

Functions

-def expose_all(root_module, container_type) +def expose_all(root_module: module, container_type: type)
-

exposes all sub-modules and namespaces to be available under given container type (class)

+

exposes all sub-modules and namespaces to be available under given container type (class)

Args

root_module : types.ModuleType
module
ns_type : type
namepace type (class)
-
+
-Source code + +Expand source code +
def expose_all(root_module: types.ModuleType, container_type: type):
     """
     exposes all sub-modules and namespaces to be available under given container type (class)
@@ -170,10 +176,10 @@ 

Args

-def get_container_type(full_name) +def get_container_type(full_name: str) ‑> type
-

get a new type to be used as a container

+

get a new type to be used as a container

Args

full_name : str
@@ -181,11 +187,13 @@

Args

Returns

-
type
+
type
a new type
-
+
-Source code + +Expand source code +
def get_container_type(full_name: str) -> type:
     """
     get a new type to be used as a container
@@ -205,9 +213,11 @@ 

Returns

def get_lazy_import_property(name, root_module, container_type)
-
+
-Source code + +Expand source code +
def get_lazy_import_property(name, root_module, container_type):
     def getter(self):
         inner_name = f"__{name}"
@@ -242,12 +252,13 @@ 

Classes

class J -(*args, **kwargs)
-
+
-Source code + +Expand source code +
class J:
     @property
     def logger(self):
@@ -269,9 +280,11 @@ 

Instance variables

var application
-
+
-Source code + +Expand source code +
@property
 def application(self):
     return self.core.application
@@ -279,9 +292,11 @@

Instance variables

var clients
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -308,9 +323,11 @@ 

Instance variables

var config
-
+
-Source code + +Expand source code +
@property
 def config(self):
     return self.core.config
@@ -318,9 +335,11 @@

Instance variables

var core
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -347,9 +366,11 @@ 

Instance variables

var data
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -376,9 +397,11 @@ 

Instance variables

var entry_points
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -405,9 +428,11 @@ 

Instance variables

var exceptions
-
+
-Source code + +Expand source code +
@property
 def exceptions(self):
     return self.core.exceptions
@@ -415,9 +440,11 @@

Instance variables

var install
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -444,9 +471,11 @@ 

Instance variables

var logger
-
+
-Source code + +Expand source code +
@property
 def logger(self):
     return self.core.logging
@@ -454,9 +483,11 @@

Instance variables

var sals
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -483,9 +514,11 @@ 

Instance variables

var servers
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -512,9 +545,11 @@ 

Instance variables

var shell
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -541,9 +576,11 @@ 

Instance variables

var threesdk
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -570,9 +607,11 @@ 

Instance variables

var tools
-
+
-Source code + +Expand source code +
def getter(self):
     inner_name = f"__{name}"
     if hasattr(self, inner_name):
@@ -647,9 +686,7 @@ 

J<

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/sals/fs/index.html b/docs/api/jumpscale/sals/fs/index.html index 6fa101e6c..725c99bfd 100644 --- a/docs/api/jumpscale/sals/fs/index.html +++ b/docs/api/jumpscale/sals/fs/index.html @@ -3,16 +3,18 @@ - + jumpscale.sals.fs API documentation - - - - - + + + + + + +
@@ -63,7 +65,7 @@

File paths exists or not

True

Reading/Writing to a file

-

There're some helpers around reading/writing text, binary like read_text, read_binary, read_file, write_text, write_binary, touch

+

There're some helpers around reading/writing text, binary like read_text(), read_bytes(), read_text(), write_text(), write_bytes(), touch()

Touching a new file

> j.sals.fs.touch("/home/rafy/testing_touch")
 
@@ -144,7 +146,7 @@

Walk over on files only

Walk over on dirs only

for el in walk('/tmp', filter_fun=j.sals.fs.is_dir) : ..
 
-

or more specific function walk_dirs

+

or more specific function walk_dirs()

for el in walk_dirs('/tmp') : ..
 
 
@@ -154,7 +156,9 @@

walk over with a bit complex filter

There are more functionality available in the SAL j.sals.fs make sure you check the API documentation for more.

-Source code + +Expand source code +
"""This module is providing everything needed for decent filesystem management
 # Using System Fs
 
@@ -1303,10 +1307,10 @@ 

walk over with a bit complex filter

Functions

-def absolute(path) +def absolute(path: str) ‑> str
-

[summary] +

[summary] e.g j.sals.fs.absolute(".") -> '/home/xmonader/js-ng'

Args

@@ -1316,11 +1320,13 @@

Args

Returns

-
str
+
str
the absolute path for relative path.
-
+
-Source code + +Expand source code +
def absolute(path: str) -> str:
     """[summary]
     e.g
@@ -1335,10 +1341,10 @@ 

Returns

-def change_dir(path) +def change_dir(path: str) ‑> str
-

Change current working directory to path +

Change current working directory to path e.g j.sals.fs.change_dir("/home/rafy/Documents") -> '/home/rafy/Documents'

@@ -1349,11 +1355,13 @@

Args

Returns

-
str
+
str
new current working dir
-
+
-Source code + +Expand source code +
def change_dir(path: str) -> str:
     """Change current working directory to `path`
     e.g
@@ -1373,18 +1381,20 @@ 

Returns

def change_filenames(from_, to, where)
-
+
-Source code + +Expand source code +
def change_filenames(from_, to, where):
     pass
-def chmod(path, mode) +def chmod(path: str, mode)
-

change file mode for path to mode +

change file mode for path to mode e.g j.sals.fs.chmod("/home/rafy/testing_dir",777)

Args

@@ -1393,9 +1403,11 @@

Args

path
mode : int
file mode
-
+
-Source code + +Expand source code +
def chmod(path: str, mode):
     """change file mode for path to mode
     e.g
@@ -1413,18 +1425,20 @@ 

Args

def chown()
-
+
-Source code + +Expand source code +
def chown():
     raise NotImplementedError()
-def copy_file(src, dst, times=False, perms=False) +def copy_file(src: str, dst: str, times=False, perms=False)
-

Copy the file, optionally copying the permission bits (mode) and +

Copy the file, optionally copying the permission bits (mode) and last access/modify time. If the destination file exists, it will be replaced. Raises OSError if the destination is a directory. If the platform does not have the ability to set the permission or times, @@ -1440,9 +1454,11 @@

Args

source path
dst : str
destination
-
+
-Source code + +Expand source code +
def copy_file(src: str, dst: str, times=False, perms=False):
     """Copy the file, optionally copying the permission bits (mode) and
         last access/modify time. If the destination file exists, it will be
@@ -1466,10 +1482,10 @@ 

Args

-def copy_stat(src, dst, times=True, perms=True) +def copy_stat(src: str, dst: str, times=True, perms=True)
-

Copy stat of src to dst

+

Copy stat of src to dst

Args

src : str
@@ -1480,9 +1496,11 @@

Args

Defaults to True.
perms : bool, optional
permissions Defaults to True.
-
+
-Source code + +Expand source code +
def copy_stat(src: str, dst: str, times=True, perms=True):
     """Copy stat of src to dst
 
@@ -1501,20 +1519,22 @@ 

Args

-def cwd() +def cwd() ‑> str
-

Return current working directory. +

Return current working directory. e.g j.sals.fs.cwd() -> '/home/rafy'

Returns

-
str
+
str
current directory
-
+
-Source code + +Expand source code +
def cwd() -> str:
     """Return current working directory.
     e.g
@@ -1531,18 +1551,20 @@ 

Returns

def default_filter_fun(entry)
-
+
-Source code + +Expand source code +
def default_filter_fun(entry):
     return True
-def exists(path) +def exists(path: str) ‑> bool
-

Checks if path exists +

Checks if path exists e.g j.sals.fs.exists("/home/rafy/testing_make_dir/test1") -> True @@ -1555,11 +1577,13 @@

Args

Returns

-
bool
+
bool
True if exists
-
+
-Source code + +Expand source code +
def exists(path: str) -> bool:
     """Checks if path exists
     e.g
@@ -1576,10 +1600,10 @@ 

Returns

-def expanduser(path) +def expanduser(path: str) ‑> str
-

Expands the tilde ~ to username +

Expands the tilde ~ to username e.g j.sals.fs.expanduser("~/work") -> '/home/xmonader/work'

Args

@@ -1589,11 +1613,13 @@

Args

Returns

-
str
+
str
path with tilde ~ resolved.
-
+
-Source code + +Expand source code +
def expanduser(path: str) -> str:
     """Expands the tilde `~` to username
     e.g
@@ -1608,10 +1634,10 @@ 

Returns

-def ext(path, include_dot=True) +def ext(path: str, include_dot=True)
-

Gets the extension of path +

Gets the extension of path e.g '/home/ahmed/myfile.py' -> .py if include_dot else py

Args

@@ -1623,11 +1649,13 @@

Args

Returns

-
str
+
str
extension
-
+
-Source code + +Expand source code +
def extension(path: str, include_dot=True):
     """Gets the extension of path
     e.g
@@ -1652,10 +1680,10 @@ 

Returns

-def extension(path, include_dot=True) +def extension(path: str, include_dot=True)
-

Gets the extension of path +

Gets the extension of path e.g '/home/ahmed/myfile.py' -> .py if include_dot else py

Args

@@ -1667,11 +1695,13 @@

Args

Returns

-
str
+
str
extension
-
+
-Source code + +Expand source code +
def extension(path: str, include_dot=True):
     """Gets the extension of path
     e.g
@@ -1699,7 +1729,7 @@ 

Returns

def fs_check(**arguments)
-

Abstracts common checks over your file system related functions. +

Abstracts common checks over your file system related functions. To reduce the boilerplate of expanding paths, checking for existence or ensuring non empty values.

Checks are defined for each argument separately in a form of a set e.g @@ -1709,14 +1739,16 @@

Returns

- required: ensures the argument is passed with a non-empty value. - expand : expands the tilde ~ in path. -- exists() +- exists() : the path exists. - file : the path is a file. - dir -: the path is a dir.

+: the path is a dir.

-Source code + +Expand source code +
def fs_check(**arguments):
     """Abstracts common checks over your file system related functions.
     To reduce the boilerplate of expanding paths, checking for existence or ensuring non empty values.
@@ -1797,29 +1829,31 @@ 

Returns

-def get_temp_dirname(suffix=None, prefix=None, dir=None) +def get_temp_dirname(suffix=None, prefix=None, dir=None) ‑> str
-

Get temp directory name +

Get temp directory name e.g j.sals.fs.get_temp_dirname(dir="/home/rafy") -> '/home/rafy/tmpntm2ptqy'

Args

-
suffix : [type], optional
+
suffix : [type], optional
ending suffix. Defaults to None.
-
prefix : [type], optional
+
prefix : [type], optional
prefix . Defaults to None.
-
dir : [type], optional
+
dir : [type], optional
where to create the directory. Defaults to None.

Returns

-
str
+
str
temp directory name.
-
+
-Source code + +Expand source code +
def get_temp_dirname(suffix=None, prefix=None, dir=None) -> str:
     """Get temp directory name
     e.g
@@ -1838,10 +1872,10 @@ 

Returns

-def get_temp_filename(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None) +def get_temp_filename(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None) ‑> str
-

Get temp filename +

Get temp filename e.g j.sals.fs.get_temp_filename(dir="/home/rafy/") -> '/home/rafy/tmp6x7w71ml'

@@ -1851,21 +1885,26 @@

Args

[description]. Defaults to "w+b".
buffering : int, optional
buffering. Defaults to -1.
-
encoding : [type], optional
+
encoding : [type], optional
encoding . Defaults to None.
-
newline : [type], optional
+
newline : [type], optional
Defaults to None.
-
suffix : [type], optional
+
suffix : [type], optional
ending suffix. Defaults to None.
-
prefix : [type], optional
+
prefix : [type], optional
prefix . Defaults to None.
-
dir : [type], optional
+
dir : [type], optional
where to create the file. Defaults to None.

Returns

-

[str]: temp filename

+
+
[str]
+
temp filename
+
-Source code + +Expand source code +
def get_temp_filename(mode="w+b", buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None) -> str:
     """Get temp filename
     e.g
@@ -1887,20 +1926,22 @@ 

Returns

-def home() +def home() ‑> str
-

Return the home directory +

Return the home directory e.g j.sals.fs.home() -> '/home/rafy'

Returns

-
str
+
str
home directory.
-
+
-Source code + +Expand source code +
def home() -> str:
     """Return the home directory
     e.g
@@ -1913,10 +1954,10 @@ 

Returns

-def is_absolute(path) +def is_absolute(path: str) ‑> bool
-

Checks if path is absolute +

Checks if path is absolute e.g j.sals.fs.is_absolute('/home/rafy/') -> True @@ -1929,11 +1970,13 @@

Args

Returns

-
bool
+
bool
True if absolute
-
+
-Source code + +Expand source code +
def is_absolute(path: str) -> bool:
     """Checks if path is absolute
     e.g
@@ -1950,10 +1993,10 @@ 

Returns

-def is_ascii_file(path, checksize=4096) +def is_ascii_file(path: str, checksize=4096) ‑> bool
-

Checks if file path is ascii +

Checks if file path is ascii e.g j.sals.fs.is_ascii_file(path="/home/rafy/testfile") -> True

@@ -1966,11 +2009,13 @@

Args

Returns

-
bool
+
bool
True if ascii file
-
+
-Source code + +Expand source code +
def is_ascii_file(path: str, checksize=4096) -> bool:
     """Checks if file `path` is ascii
     e.g
@@ -1996,17 +2041,19 @@ 

Returns

def is_binary_file(path)
-
+
-Source code + +Expand source code +
is_binary_file = lambda path: not is_ascii_file(path)
-

Checks if path is a broken symlink

+

Checks if path is a broken symlink

Args

path : str
@@ -2014,12 +2061,14 @@

Args

Returns

-
bool
+
bool
True if path is a broken symlink False if path not found or symlink is not broken
-
+
-Source code + +Expand source code +
def is_broken_link(path: str) -> bool:
     """Checks if path is a broken symlink
 
@@ -2034,10 +2083,10 @@ 

Returns

-def is_dir(path) +def is_dir(path: str) ‑> bool
-

Checks if path is a dir +

Checks if path is a dir e.g j.sals.fs.is_dir(path= '/home/rafy') -> True @@ -2050,11 +2099,13 @@

Args

Returns

-
bool
+
bool
True if is dir and False otherwise
-
+
-Source code + +Expand source code +
def is_dir(path: str) -> bool:
     """Checks if path is a dir
     e.g
@@ -2070,10 +2121,10 @@ 

Returns

-def is_empty_dir(path) +def is_empty_dir(path: str) ‑> bool
-

Checks if path is emptry directory +

Checks if path is emptry directory e.g j.sals.fs.is_empty_dir("/home/rafy/empty_dir") -> True @@ -2086,11 +2137,13 @@

Args

Returns

-
bool
+
bool
True if path is emptry directory
-
+
-Source code + +Expand source code +
def is_empty_dir(path: str) -> bool:
     """Checks if path is emptry directory
     e.g
@@ -2115,10 +2168,10 @@ 

Returns

-def is_file(path) +def is_file(path: str) ‑> bool
-

Checks if path is a file +

Checks if path is a file e.g j.sals.fs.is_file(path= '/home/rafy') -> False @@ -2131,11 +2184,13 @@

Args

Returns

-
bool
+
bool
True if is file and False otherwise
-
+
-Source code + +Expand source code +
def is_file(path: str) -> bool:
     """Checks if path is a file
     e.g
@@ -2152,10 +2207,10 @@ 

Returns

-def is_mount(path) +def is_mount(path: str) ‑> bool
-

Checks if path is mount

+

Checks if path is mount

Args

path : str
@@ -2163,11 +2218,13 @@

Args

Returns

-
bool
+
bool
True if mount
-
+
-Source code + +Expand source code +
def is_mount(path: str) -> bool:
     # TODO add example here
     """Checks if path is mount
@@ -2183,10 +2240,10 @@ 

Returns

-

Checks if path symlink +

Checks if path symlink e.g j.sals.fs.is_symlink('/home/rafy/testfile3') -> True @@ -2199,11 +2256,13 @@

Args

Returns

-
bool
+
bool
True if symlink False otherwise
-
+
-Source code + +Expand source code +
def is_symlink(path: str) -> bool:
     """Checks if path symlink
     e.g
@@ -2220,10 +2279,10 @@ 

Returns

-def join_paths(*paths) +def join_paths(*paths) ‑> str
-

Convert tuple of path parts into a path string +

Convert tuple of path parts into a path string e.g j.sals.fs.join_paths("home","rafy") -> 'home/rafy'

@@ -2234,11 +2293,13 @@

Args

Returns

-
str
+
str
joined path parts
-
+
-Source code + +Expand source code +
def join_paths(*paths) -> str:
     """
     Convert tuple of path parts into a path string
@@ -2257,19 +2318,21 @@ 

Returns

-def lchmod(path, mode) +def lchmod(path: str, mode)
-

change file mode for path to mode (handles links too)

+

change file mode for path to mode (handles links too)

Args

path : str
path
mode : int
file mode
-
+
-Source code + +Expand source code +
def lchmod(path: str, mode):
     """change file mode for path to mode (handles links too)
 
@@ -2282,10 +2345,10 @@ 

Args

-def lstat(path) +def lstat(path: str)
-

Gets stat of path path (handles links) +

Gets stat of path path (handles links) e.g j.sals.fs.lstat("/home/rafy/testing_link") -> os.stat_result(st_mode=41471, st_ino=7081257, st_dev=2049, st_nlink=1, st_uid=1000, st_gid=1000, st_size=16, st_atime=1586445737, st_mtime=1586445734, st_ctime=1586445734)

@@ -2296,11 +2359,13 @@

Args

Returns

-
stat_result
+
stat_result
returns stat struct.
-
+
-Source code + +Expand source code +
def lstat(path: str):
     """Gets stat of path `path` (handles links)
     e.g
@@ -2320,14 +2385,16 @@ 

Returns

def make_path(path)
-

to ensure the given path, create it if it does not exist

+

to ensure the given path, create it if it does not exist

Args

path : str
path
-
+
-Source code + +Expand source code +
def make_path(path):
     """
     to ensure the given path, create it if it does not exist
@@ -2341,10 +2408,10 @@ 

Args

-def makedirs(path, exist_ok=True) +def makedirs(path: str, exist_ok=True)
-

Creates dir as well as all non exisitng parents in the path +

Creates dir as well as all non exisitng parents in the path e.g j.sals.fs.mkdirs("/home/rafy/testing_make_dir/test1/test2",exist_ok=False) j.sals.fs.mkdirs("/home/rafy/testing_make_dir/test1/test2",exist_ok=True) @@ -2357,9 +2424,11 @@

Args

path to create dir at
exist_ok : bool, optional
won't fail if directory exists. Defaults to True.
-
+
-Source code + +Expand source code +
def mkdirs(path: str, exist_ok=True):
     """Creates dir as well as all non exisitng parents in the path
     e.g
@@ -2375,10 +2444,10 @@ 

Args

-def mkdir(path, exist_ok=True) +def mkdir(path: str, exist_ok=True)
-

Makes directory at path +

Makes directory at path e.g j.sals.fs.mkdir("/home/rafy/testing_make_dir") j.sals.fs.mkdir("/home/rafy/testing_make_dir",exist_ok=True) @@ -2391,9 +2460,15 @@

Args

exist_ok : bool, optional
won't fail if directory exists. Defaults to True.
-

Returns

+

Returns

+
+
[type]
+
[description]
+
-Source code + +Expand source code +
def mkdir(path: str, exist_ok=True):
     """Makes directory at path
     e.g
@@ -2412,10 +2487,10 @@ 

Returns

-def mkdirs(path, exist_ok=True) +def mkdirs(path: str, exist_ok=True)
-

Creates dir as well as all non exisitng parents in the path +

Creates dir as well as all non exisitng parents in the path e.g j.sals.fs.mkdirs("/home/rafy/testing_make_dir/test1/test2",exist_ok=False) j.sals.fs.mkdirs("/home/rafy/testing_make_dir/test1/test2",exist_ok=True) @@ -2428,9 +2503,11 @@

Args

path to create dir at
exist_ok : bool, optional
won't fail if directory exists. Defaults to True.
-
+
-Source code + +Expand source code +
def mkdirs(path: str, exist_ok=True):
     """Creates dir as well as all non exisitng parents in the path
     e.g
@@ -2446,10 +2523,10 @@ 

Args

-def parent(path) +def parent(path: str) ‑> str
-

Get path's parent +

Get path's parent e.g j.sals.fs.parent("/home/rafy/testing_make_dir/test1") -> '/home/rafy/testing_make_dir'

@@ -2460,11 +2537,13 @@

Args

Returns

-
str
+
str
parent path.
-
+
-Source code + +Expand source code +
def parent(path: str) -> str:
     """Get path's parent
     e.g
@@ -2480,10 +2559,10 @@ 

Returns

-def parents(path) +def parents(path: str) ‑> List[str]
-

Get parents list

+

Get parents list

e.g j.sals.fs.parents("/tmp/home/ahmed/myfile.py") -> [PosixPath('/tmp/home/ahmed'), @@ -2497,11 +2576,13 @@

Args

Returns

-
List[str]: list of parents()
-
 
-
+
List[str]
+
list of parents
+
-Source code + +Expand source code +
def parents(path: str) -> List[str]:
     """Get parents list
 
@@ -2523,25 +2604,27 @@ 

Returns

-def parts_to_path(parts) +def parts_to_path(parts: List[str]) ‑> str
-

Convert list of path parts into a path string +

Convert list of path parts into a path string e.g j.sals.fs.parts_to_path(["home","rafy"]) -> 'home/rafy'

Args

-
parts : List[str]
+
parts : List[str]
path parts

Returns

-
str
+
str
joined path parts
-
+
-Source code + +Expand source code +
def parts_to_path(parts: List[str]) -> str:
     """Convert list of path parts into a path string
     e.g
@@ -2560,10 +2643,10 @@ 

Returns

-def path_parts(path) +def path_parts(path: str) ‑> List[str]
-

Convert path to a list of parts +

Convert path to a list of parts e.g '/tmp/tmp-5383p1GOmMOOwvfi.tpl' -> ('/', 'tmp', 'tmp-5383p1GOmMOOwvfi.tpl')

@@ -2573,9 +2656,14 @@

Args

path to convert to parts

Returns

-

List[str]: path parts.

+
+
List[str]
+
path parts.
+
-Source code + +Expand source code +
def path_parts(path: str) -> List[str]:
     """Convert path to a list of parts
     e.g
@@ -2590,10 +2678,10 @@ 

Returns

-def read_ascii(path) +def read_ascii(path: str) ‑> str
-

read ascii content at path +

read ascii content at path e.g j.sals.fs.read_text("/home/rafy/testing_text.txt") -> 'hello world @@ -2603,9 +2691,11 @@

Returns

Returns: str: ascii content in path -
+
-Source code + +Expand source code +
def read_text(path: str) -> str:
     """read ascii content at `path`
     e.g
@@ -2621,10 +2711,10 @@ 

Returns

-def read_binary(path) +def read_binary(path: str) ‑> bytes
-

read binary content at path +

read binary content at path e.g j.sals.fs.read_bytes("/home/rafy/testing_text.txt") -> b'hello world @@ -2634,9 +2724,11 @@

Returns

Returns: bytes: binary content in path -
+
-Source code + +Expand source code +
def read_bytes(path: str) -> bytes:
     """read binary content at `path`
     e.g
@@ -2652,10 +2744,10 @@ 

Returns

-def read_bytes(path) +def read_bytes(path: str) ‑> bytes
-

read binary content at path +

read binary content at path e.g j.sals.fs.read_bytes("/home/rafy/testing_text.txt") -> b'hello world @@ -2665,9 +2757,11 @@

Returns

Returns: bytes: binary content in path -
+
-Source code + +Expand source code +
def read_bytes(path: str) -> bytes:
     """read binary content at `path`
     e.g
@@ -2683,10 +2777,10 @@ 

Returns

-def read_file(path) +def read_file(path: str) ‑> str
-

read ascii content at path +

read ascii content at path e.g j.sals.fs.read_text("/home/rafy/testing_text.txt") -> 'hello world @@ -2696,9 +2790,11 @@

Returns

Returns: str: ascii content in path -
+
-Source code + +Expand source code +
def read_text(path: str) -> str:
     """read ascii content at `path`
     e.g
@@ -2714,10 +2810,10 @@ 

Returns

-def read_file_binary(path) +def read_file_binary(path: str) ‑> bytes
-

read binary content at path +

read binary content at path e.g j.sals.fs.read_bytes("/home/rafy/testing_text.txt") -> b'hello world @@ -2727,9 +2823,11 @@

Returns

Returns: bytes: binary content in path -
+
-Source code + +Expand source code +
def read_bytes(path: str) -> bytes:
     """read binary content at `path`
     e.g
@@ -2748,18 +2846,20 @@ 

Returns

def read_link(path)
-
+
-Source code + +Expand source code +
def read_link(path):
     raise NotImplementedError()
-def read_text(path) +def read_text(path: str) ‑> str
-

read ascii content at path +

read ascii content at path e.g j.sals.fs.read_text("/home/rafy/testing_text.txt") -> 'hello world @@ -2769,9 +2869,11 @@

Returns

Returns: str: ascii content in path -
+
-Source code + +Expand source code +
def read_text(path: str) -> str:
     """read ascii content at `path`
     e.g
@@ -2790,18 +2892,20 @@ 

Returns

def remove_links(path)
-
+
-Source code + +Expand source code +
def remove_links(path):
     raise NotImplementedError()
-def rename(path1, path2) +def rename(path1: str, path2: str)
-

Rename path1 to path2 +

Rename path1 to path2 e.g j.sals.fs.rename("/home/rafy/testing_make_dir","/home/rafy/testing_dir")

Args

@@ -2810,9 +2914,11 @@

Args

source path
path2 : str
dest path
-
+
-Source code + +Expand source code +
def rename(path1: str, path2: str):
     """Rename path1 to path2
     e.g
@@ -2830,18 +2936,20 @@ 

Args

def replace_words_in_files(from_, to, where)
-
+
-Source code + +Expand source code +
def replace_words_in_files(from_, to, where):
     pass
-def resolve(path) +def resolve(path: str) ‑> str
-

resolve . and .. in path +

resolve . and .. in path e.g j.sals.fs.resolve("") -> PosixPath('/home/rafy/Documents') @@ -2854,11 +2962,13 @@

Args

Returns

-
str
+
str
resolved path
-
+
-Source code + +Expand source code +
def resolve(path: str) -> str:
     """resolve `.` and `..` in path
     e.g
@@ -2875,10 +2985,10 @@ 

Returns

-

Remove broken symlink

+

Remove broken symlink

Args

path : str
@@ -2886,11 +2996,13 @@

Args

Returns

-
bool
+
bool
True if broken symlink removed
-
+
-Source code + +Expand source code +
def rm_broken_link(path: str) -> bool:
     """Remove broken symlink
 
@@ -2908,19 +3020,21 @@ 

Returns

-def rm_emptry_dir(path) +def rm_emptry_dir(path: str)
-

Remove empty directory if the directory is not empty it will throw exception (Directory not empty) +

Remove empty directory if the directory is not empty it will throw exception (Directory not empty) e.g j.sals.fs.rm_emptry_dir("/home/rafy/empty_dir")

Args

path : str
path to remove.
-
+
-Source code + +Expand source code +
def rm_emptry_dir(path: str):
     """Remove empty directory if the directory is not empty it will throw exception (Directory not empty)
     e.g
@@ -2934,17 +3048,19 @@ 

Args

-def rmtree(path) +def rmtree(path: str)
-

Remove directory tree

+

Remove directory tree

Args

path : str
path to remove
-
+
-Source code + +Expand source code +
def rmtree(path: str):
     """Remove directory tree
     Args:
@@ -2958,10 +3074,10 @@ 

Args

-def stat(path) +def stat(path: str)
-

Gets stat of path path +

Gets stat of path path e.g j.sals.fs.stat("/home/rafy/test_dir/test") -> os.stat_result(st_mode=33204, st_ino=795348, st_dev=2049, st_nlink=1, st_uid=1000, st_gid=1000, st_size=0, st_atime=1586445434, st_mtime=1586445434, st_ctime=1586445434)

@@ -2972,11 +3088,13 @@

Args

Returns

-
stat_result
+
stat_result
returns stat struct.
-
+
-Source code + +Expand source code +
def stat(path: str):
     """Gets stat of path `path`
     e.g
@@ -2992,10 +3110,10 @@ 

Returns

-def stem(path) +def stem(path: str) ‑> str
-

returns the stem of a path (path without parent directory and without extension) +

returns the stem of a path (path without parent directory and without extension) e.g j.sals.fs.stem("/tmp/tmp-5383p1GOmMOOwvfi.tpl") -> 'tmp-5383p1GOmMOOwvfi'

@@ -3006,11 +3124,13 @@

Args

Returns

-
str
+
str
path without parent directory and without extension
-
+
-Source code + +Expand source code +
def stem(path: str) -> str:
     """returns the stem of a path (path without parent directory and without extension)
     e.g
@@ -3026,10 +3146,10 @@ 

Returns

-

Create a symbolic link. +

Create a symbolic link. e.g j.sals.fs.symlink(src="/home/rafy/testing_text.txt",dst="/home/rafy/link_test")

Args

@@ -3040,9 +3160,11 @@

Args

Destination path of link
overwrite : bool, optional
If link exists will delete it. Defaults to False.
-
+
-Source code + +Expand source code +
def symlink(src: str, dst: str, overwrite=False):
     """Create a symbolic link.
     e.g
@@ -3060,19 +3182,21 @@ 

Args

-def touch(path) +def touch(path: str)
-

create file +

create file e.g j.sals.fs.touch("/home/rafy/testing_touch")

Args

path : str
path to create file
-
+
-Source code + +Expand source code +
def touch(path: str):
     """create file
     e.g
@@ -3086,19 +3210,21 @@ 

Args

-

unlink path +

unlink path e.g j.sals.fs.unlink("/home/rafy/testfile3")

Args

path : str
path to unlink
-
+
-Source code + +Expand source code +
def unlink(path: str):
     """unlink path
     e.g
@@ -3110,10 +3236,10 @@ 

Args

-def walk(path, pat='*', filter_fun=<function default_filter_fun>) +def walk(path: str, pat='*', filter_fun=<function default_filter_fun>)
-

walk recursively on path +

walk recursively on path e.g for el in walk('/tmp', filter_fun=j.sals.fs.is_file) : .. for el in walk('/tmp', filter_fun=j.sals.fs.is_dir) : .. @@ -3126,9 +3252,11 @@

Args

pattern to match against. Defaults to "*".
filter_fun : Function, optional
filtering function. Defaults to default_filter_fun which accepts anything.
-
+
-Source code + +Expand source code +
def walk(path: str, pat="*", filter_fun=default_filter_fun):
     """walk recursively on path
     e.g
@@ -3153,7 +3281,7 @@ 

Args

def walk_dirs(path, recursive=True)
-

walk over directories in path and applies function fun +

walk over directories in path and applies function fun e.g

for el in walk_dirs('/tmp') : ..
 
@@ -3163,9 +3291,11 @@

Args

path to walk over
recursive : bool, optional
recursive or not. Defaults to True.
-
+
-Source code + +Expand source code +
def walk_dirs(path, recursive=True):
     """
         walk over directories in path and applies function `fun`
@@ -3187,10 +3317,10 @@ 

Args

-def walk_files(path, recursive=True) +def walk_files(path: str, recursive=True)
-

walk over files in path and applies function fun +

walk over files in path and applies function fun e.g

for el in walk_files('/tmp') : ..
 
@@ -3200,9 +3330,11 @@

Args

path to walk over
recursive : bool, optional
recursive or not. Defaults to True.
-
+
-Source code + +Expand source code +
def walk_files(path: str, recursive=True):
     """
     walk over files in path and applies function `fun`
@@ -3224,10 +3356,10 @@ 

Args

-def walk_non_recursive(path, filter_fun=<function default_filter_fun>) +def walk_non_recursive(path: str, filter_fun=<function default_filter_fun>)
-

walks non recursively on path +

walks non recursively on path e.g for el in walk('/tmp', filter=j.sals.fs.is_file) : .. for el in walk('/tmp', filter=j.sals.fs.is_dir) : .. @@ -3240,9 +3372,11 @@

Args

pattern to match against. Defaults to "*".
filter_fun : Function, optional
filtering function. Defaults to default_filter_fun which accepts anything.
-
+
-Source code + +Expand source code +
def walk_non_recursive(path: str, filter_fun=default_filter_fun):
     """walks non recursively on path
     e.g
@@ -3263,10 +3397,10 @@ 

Args

-def write_ascii(path, data, encoding=None, append=False) +def write_ascii(path: str, data: str, encoding=None, append=False)
-

write text data to path path with encoding +

write text data to path path with encoding e.g j.sals.fs.write_text(path="/home/rafy/testing_text.txt",data="hello world") -> 11

@@ -3276,18 +3410,20 @@

Args

path to write to
data : str
ascii content
-
encoding : [type], optional
+
encoding : [type], optional
encoding. Defaults to None.
append : bool, optional
indicate whether to open the file in append mode or not.

Returns

-
int
+
int
returning the number of characters written.
-
+
-Source code + +Expand source code +
def write_text(path: str, data: str, encoding=None, append=False):
     """write text `data` to path `path` with encoding
     e.g
@@ -3310,10 +3446,10 @@ 

Returns

-def write_binary(path, data, append=False) +def write_binary(path: str, data: bytes, append=False)
-

write binary data to path path

+

write binary data to path path

If file does not exist, it creates a new file. If file exists it truncates the file unless append arg is True e.g j.sals.fs.write_bytes(path="/home/rafy/testing_text.txt",data=b"hello world") @@ -3329,11 +3465,13 @@

Args

Returns

-
int
+
int
returning the number of characters written.
-
+
-Source code + +Expand source code +
def write_bytes(path: str, data: bytes, append=False):
     """write binary `data` to path `path`
 
@@ -3356,10 +3494,10 @@ 

Returns

-def write_bytes(path, data, append=False) +def write_bytes(path: str, data: bytes, append=False)
-

write binary data to path path

+

write binary data to path path

If file does not exist, it creates a new file. If file exists it truncates the file unless append arg is True e.g j.sals.fs.write_bytes(path="/home/rafy/testing_text.txt",data=b"hello world") @@ -3375,11 +3513,13 @@

Args

Returns

-
int
+
int
returning the number of characters written.
-
+
-Source code + +Expand source code +
def write_bytes(path: str, data: bytes, append=False):
     """write binary `data` to path `path`
 
@@ -3402,10 +3542,10 @@ 

Returns

-def write_file(path, data, encoding=None, append=False) +def write_file(path: str, data: str, encoding=None, append=False)
-

write text data to path path with encoding +

write text data to path path with encoding e.g j.sals.fs.write_text(path="/home/rafy/testing_text.txt",data="hello world") -> 11

@@ -3415,18 +3555,20 @@

Args

path to write to
data : str
ascii content
-
encoding : [type], optional
+
encoding : [type], optional
encoding. Defaults to None.
append : bool, optional
indicate whether to open the file in append mode or not.

Returns

-
int
+
int
returning the number of characters written.
-
+
-Source code + +Expand source code +
def write_text(path: str, data: str, encoding=None, append=False):
     """write text `data` to path `path` with encoding
     e.g
@@ -3449,10 +3591,10 @@ 

Returns

-def write_file_binary(path, data, append=False) +def write_file_binary(path: str, data: bytes, append=False)
-

write binary data to path path

+

write binary data to path path

If file does not exist, it creates a new file. If file exists it truncates the file unless append arg is True e.g j.sals.fs.write_bytes(path="/home/rafy/testing_text.txt",data=b"hello world") @@ -3468,11 +3610,13 @@

Args

Returns

-
int
+
int
returning the number of characters written.
-
+
-Source code + +Expand source code +
def write_bytes(path: str, data: bytes, append=False):
     """write binary `data` to path `path`
 
@@ -3495,10 +3639,10 @@ 

Returns

-def write_text(path, data, encoding=None, append=False) +def write_text(path: str, data: str, encoding=None, append=False)
-

write text data to path path with encoding +

write text data to path path with encoding e.g j.sals.fs.write_text(path="/home/rafy/testing_text.txt",data="hello world") -> 11

@@ -3508,18 +3652,20 @@

Args

path to write to
data : str
ascii content
-
encoding : [type], optional
+
encoding : [type], optional
encoding. Defaults to None.
append : bool, optional
indicate whether to open the file in append mode or not.

Returns

-
int
+
int
returning the number of characters written.
-
+
-Source code + +Expand source code +
def write_text(path: str, data: str, encoding=None, append=False):
     """write text `data` to path `path` with encoding
     e.g
@@ -3666,9 +3812,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/sals/hostsfile/index.html b/docs/api/jumpscale/sals/hostsfile/index.html index 06ca28424..ead294107 100644 --- a/docs/api/jumpscale/sals/hostsfile/index.html +++ b/docs/api/jumpscale/sals/hostsfile/index.html @@ -3,15 +3,17 @@ - + jumpscale.sals.hostsfile API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.sals.hostsfile

-Source code + +Expand source code +
import re
 
 
@@ -77,7 +81,7 @@ 

Module jumpscale.sals.hostsfile

update the hostname for ip Args: ip (str) : the ip address - domain (str) : the host name + domain (str) : the host name """ self.content[ip] = domain @@ -116,9 +120,11 @@

Classes

(hosts_file_path)
-
+
-Source code + +Expand source code +
class HostsFile:
     def __init__(self, hosts_file_path):
         self.path = hosts_file_path
@@ -171,7 +177,7 @@ 

Classes

update the hostname for ip Args: ip (str) : the ip address - domain (str) : the host name + domain (str) : the host name """ self.content[ip] = domain @@ -201,12 +207,14 @@

Methods

def add(self, ip, domain)
-

add new entry to the hosts file

+

add new entry to the hosts file

Args

ip (str) : the ip address -domain (str) : the host name

+domain (str) : the host name

-Source code + +Expand source code +
def add(self, ip, domain):
     """
     add new entry to the hosts file
@@ -221,13 +229,15 @@ 

Args

def exists(self, ip)
-

check for the existence of the ip in hosts file.

+

check for the existence of the ip in hosts file.

Args

ip (str) : the ip address

Return

-

boolen expression

+

boolen expression

-Source code + +Expand source code +
def exists(self, ip):
     """
     check for the existence of the ip in hosts file.
@@ -243,16 +253,15 @@ 

Return

def get_hostname(self, ip)
-

get the hostname for ip

+

get the hostname for ip

Args

ip (str) : the ip address

Returns

-
-
the hostname for the ip address
-
 
-
+

the hostname for the ip address

-Source code + +Expand source code +
def get_hostname(self, ip):
     """
     get the hostname for ip
@@ -268,11 +277,13 @@ 

Returns

def remove(self, ip)
-

remove the ip and its hostname from hosts file

+

remove the ip and its hostname from hosts file

Args

-

ip (str) : the ip address

+

ip (str) : the ip address

-Source code + +Expand source code +
def remove(self, ip):
     """
     remove the ip and its hostname from hosts file
@@ -286,18 +297,20 @@ 

Args

def set_hostname(self, ip, domain)
-

update the hostname for ip

+

update the hostname for ip

Args

ip (str) : the ip address -domain (str) : the host name

+domain (str) : the host name

-Source code + +Expand source code +
def set_hostname(self, ip, domain):
     """
     update the hostname for ip
     Args:
         ip (str) : the ip address
-        domain (str) : the host name           
+        domain (str) : the host name
     """
     self.content[ip] = domain
@@ -306,9 +319,11 @@

Args

def write(self)
-

write the changes into the file.

+

write the changes into the file.

-Source code + +Expand source code +
def write(self):
     """
     write the changes into the file.
@@ -355,9 +370,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/sals/index.html b/docs/api/jumpscale/sals/index.html index 0430e07ae..5d6ddb85b 100644 --- a/docs/api/jumpscale/sals/index.html +++ b/docs/api/jumpscale/sals/index.html @@ -3,15 +3,17 @@ - + jumpscale.sals API documentation - - - - - + + + + + + +
@@ -26,28 +28,28 @@

Sub-modules

jumpscale.sals.fs
-

This module is providing everything needed for decent filesystem management -Using System Fs …

+

This module is providing everything needed for decent filesystem management +Using System Fs …

jumpscale.sals.hostsfile
-
+
jumpscale.sals.nettools
-

This module contains a collection of functions which help in manage network connections and interfaces …

+

This module contains a collection of functions which help in manage network connections and interfaces …

jumpscale.sals.process
-

This module execute process on system and manage them …

+

This module execute process on system and manage them …

jumpscale.sals.testdocs
-
+
jumpscale.sals.unix
-
+

@@ -83,9 +85,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/sals/nettools/index.html b/docs/api/jumpscale/sals/nettools/index.html index f4953f145..7566febf9 100644 --- a/docs/api/jumpscale/sals/nettools/index.html +++ b/docs/api/jumpscale/sals/nettools/index.html @@ -3,15 +3,17 @@ - + jumpscale.sals.nettools API documentation - - - - - + + + + + + +
@@ -28,7 +30,9 @@

Module jumpscale.sals.nettools

For deterministic behavior use a numeric address in host portion. https://docs.python.org/3/library/socket.html

-Source code + +Expand source code +
"""This module contains a collection of functions which help in manage network connections and interfaces.
 
 General Note on python socket operations:
@@ -722,10 +726,10 @@ 

Module jumpscale.sals.nettools

Functions

-def check_url_reachable(url, timeout=5, verify=True, fake_user_agent=True) +def check_url_reachable(url: str, timeout: Optional[int] = 5, verify: Optional[bool] = True, fake_user_agent: Optional[bool] = True) ‑> bool
-

Check that given url is reachable

+

Check that given url is reachable

Args

url : str
@@ -739,16 +743,18 @@

Args

Raises

-
ValueError
+
ValueError
raises if not correct url

Returns

-
bool
+
bool
True if the test succeeds, False otherwise
-
+
-Source code + +Expand source code +
def check_url_reachable(
     url: str, timeout: Optional[int] = 5, verify: Optional[bool] = True, fake_user_agent: Optional[bool] = True
 ) -> bool:
@@ -793,10 +799,10 @@ 

Returns

-def download(url, localpath='', username=None, passwd=None, overwrite=True, append_to_home=False, name_from_url=True) +def download(url: str, localpath: Optional[str] = '', username: Optional[str] = None, passwd: Optional[str] = None, overwrite: Optional[bool] = True, append_to_home: Optional[bool] = False, name_from_url: Optional[bool] = True)
-

Download a url to a file or a directory, supported protocols: http, https, ftp, file

+

Download a url to a file or a directory, supported protocols: http, https, ftp, file

Args

url : str
@@ -820,20 +826,20 @@

Args

Raises

-
PermissionError
+
PermissionError
[description]
-
FileNotFoundError
+
FileNotFoundError
[description]
-
FileExistsError
+
FileExistsError
[description]
-
ValueError
+
ValueError
[description]
-
URLError
+
URLError
[description]

Returns

-
namedtuple
+
namedtuple
namedtuple('DownloadResult', ['localpath', 'content', content_length]) - localpath (pathlib.Path) - content (bytes): only if localpath is None else it will be None always @@ -847,11 +853,13 @@

Todo

Examples

use default values for args will download the url to cwd and get the name from the url,

if the file already exists, it will overwritten.

-
>>> nettools.download('https://www.7-zip.org/a/7z1900-extra.7z')
+
>>> nettools.download('https://www.7-zip.org/a/7z1900-extra.7z')
 DownloadResult(localpath=PosixPath('/home/sameh/projects/js-ng/7z1900-extra.7z'), content=None, content_length='929117')
-
+

-Source code + +Expand source code +
def download(
     url: str,
     localpath: Optional[str] = "",
@@ -977,10 +985,10 @@ 

if the file already exis

-def get_default_ip_config(ip='8.8.8.8') +def get_default_ip_config(ip: Optional[str] = '8.8.8.8') ‑> tuple
-

get default nic and address, by default, the one exposed to internet

+

get default nic and address, by default, the one exposed to internet

Args

ip : str
@@ -988,18 +996,20 @@

Args

Raises

-
ValueError
+
ValueError
if address does not represent a valid IPv4 or IPv6 address.
-
RuntimeError
+
RuntimeError
if can't connect

Returns

-
tuple
+
tuple
default nic name and its ip address
-
+
-Source code + +Expand source code +
def get_default_ip_config(ip: Optional[str] = "8.8.8.8") -> tuple:
     """get default nic and address, by default, the one exposed to internet
 
@@ -1028,10 +1038,10 @@ 

Returns

-def get_free_port(ipv6=False, udp=False, return_socket=False) +def get_free_port(ipv6: Optional[bool] = False, udp: Optional[bool] = False, return_socket: Optional[bool] = False)
-

Bind an ipv4 or ipv6 socket to port 0 to make OS pick a random, free and +

Bind an ipv4 or ipv6 socket to port 0 to make OS pick a random, free and available port from 1024 to 65535.

you can optionally choose to reuse the socket by set return_socket to True (preferred to To prevent race conditions from occurring) but then it is your responsibility to close @@ -1048,27 +1058,29 @@

Args

Returns

-
int
+
int
returns a random free port from 1024 to 65535 range.
+
Optional[socket]
+
in the case of return_socket set to True, socket will be returned alongside the port in a tuple.
-

Optional[socket]: in the case of return_socket set to True, socket will be returned alongside the port in a tuple.

Example

get a free TCP port that currently not binded to 127.0.0.1

-
>>> port = get_free_port()
+
>>> port = get_free_port()
 # get a free UDP port that currently not binded to 127.0.0.1
 >>> port = get_free_port(udp=True)
 # get a free TCP port that currently not binded to ::1
 >>> port = get_free_port(ipv6=True)
 # get a free TCP port that currently not binded to 127.0.0.1
-
-

and reuse the socket instead of creating a socket and bind it to selected port

-
>>> port, sock = get_free_port(return_socket=True)
+# and reuse the socket instead of creating a socket and bind it to selected port
+>>> port, sock = get_free_port(return_socket=True)
 >>> sock.listen()
 ..
 >>> sock.close()
-
+
-Source code + +Expand source code +
def get_free_port(ipv6: Optional[bool] = False, udp: Optional[bool] = False, return_socket: Optional[bool] = False):
     """Bind an ipv4 or ipv6 socket to port 0 to make OS pick a random, free and
     available port from 1024 to 65535.
@@ -1117,10 +1129,10 @@ 

-def get_host_by_name(dnsHostname) +def get_host_by_name(dnsHostname: str) ‑> str
-

get host address by its name

+

get host address by its name

Args

dnsHostname : str
@@ -1128,11 +1140,13 @@

Args

Returns

-
str
+
str
host address
-
+
-Source code + +Expand source code +
def get_host_by_name(dnsHostname: str) -> str:  # pragma: no cover - we're just proxying
     """get host address by its name
 
@@ -1146,17 +1160,19 @@ 

Returns

-def get_host_name() +def get_host_name() ‑> str
-

Get hostname of the machine

+

Get hostname of the machine

Returns

-
str
+
str
host name
-
+
-Source code + +Expand source code +
def get_host_name() -> str:  # pragma: no cover - we're just proxying
     """Get hostname of the machine
 
@@ -1167,10 +1183,10 @@ 

Returns

-def get_mac_address(interface) +def get_mac_address(interface: str) ‑> str
-

Return the MAC address of this interface

+

Return the MAC address of this interface

Args

interface : str
@@ -1178,11 +1194,13 @@

Args

Returns

-
str
+
str
mac of the interface
-
+
-Source code + +Expand source code +
def get_mac_address(interface: str) -> str:
     """Return the MAC address of this interface
 
@@ -1196,10 +1214,10 @@ 

Returns

-def get_network_info(device=None) +def get_network_info(device: Optional[str] = None) ‑> list
-

Get network info

+

Get network info

Args

device : str, optional
@@ -1207,23 +1225,25 @@

Args

Raises

-
Runtime
+
Runtime
if it could not find the specified device
-
NotImplementedError
+
NotImplementedError
if the function runs on unsupported OS

Returns

-
Dict, or list of dicts if device arg used: network info
-
 
+
Dict, or list of dicts if device arg used
+
network info

[{'ip': [('127.0.0.1', 8)], 'ip6': [('::1', 128)], 'mac': '00:00:00:00:00:00', 'name': 'lo'}, {'ip': [('192.168.1.6', 24)], 'ip6': [('fdb4:f58e:3c34:300:91f0:9c76:e1fb:d060', 64), …], 'mac': 'd8:9c:67:2a:f2:53', -'name': 'wlp3s0'}, …]

+'name': 'wlp3s0'}, …]

-Source code + +Expand source code +
def get_network_info(device: Optional[str] = None) -> list:
     """Get network info
 
@@ -1293,17 +1313,19 @@ 

Returns

-def get_nic_names() +def get_nic_names() ‑> list
-

Get Nics on this machine

+

Get Nics on this machine

Returns

-
list
+
list
list of all availabe nics
-
+
-Source code + +Expand source code +
def get_nic_names() -> list:
     """Get Nics on this machine
 
@@ -1314,10 +1336,10 @@ 

Returns

-def get_nic_type(interface) +def get_nic_type(interface: str) ‑> str
-

Get Nic Type on a certain interface

+

Get Nic Type on a certain interface

Args

interface : str
@@ -1325,18 +1347,20 @@

Args

Raises

-
Runtime
+
Runtime
if ethtool not installed on the system
-
Value
+
Value
if interface given is invalid

Returns

-
str
+
str
type of the interface
-
+
-Source code + +Expand source code +
def get_nic_type(interface: str) -> str:
     """Get Nic Type on a certain interface
 
@@ -1399,10 +1423,10 @@ 

Returns

-def get_reachable_ip_address(ip, port=0) +def get_reachable_ip_address(ip: str, port: Optional[int] = 0) ‑> str
-

figures out what source address would be used if some traffic were to be sent out to specified ip. +

figures out what source address would be used if some traffic were to be sent out to specified ip. compatible with both IPv4 and IPv6.

Args

@@ -1413,18 +1437,20 @@

Args

Raises

-
ValueError
+
ValueError
if address does not represent a valid IPv4 or IPv6 address, or port is invalid.
-
RuntimeError
+
RuntimeError
if can't connect

Returns

-
str
+
str
ip that can connect to the specified ip
-
+
-Source code + +Expand source code +
def get_reachable_ip_address(ip: str, port: Optional[int] = 0) -> str:
     """figures out what source address would be used if some traffic were to be sent out to specified ip.
     compatible with both IPv4 and IPv6.
@@ -1462,10 +1488,10 @@ 

Returns

-def is_nic_connected(interface) +def is_nic_connected(interface: str) ‑> bool
-

check if interface is connected

+

check if interface is connected

Args

interface : str
@@ -1473,11 +1499,13 @@

Args

Returns

-
bool
+
bool
whether it is connected or not
-
+
-Source code + +Expand source code +
def is_nic_connected(interface: str) -> bool:
     """check if interface is connected
 
@@ -1507,26 +1535,28 @@ 

Returns

-def netrange_get(device, skip_begin=10, skip_end=10) +def netrange_get(device: str, skip_begin: Optional[int] = 10, skip_end: Optional[int] = 10) ‑> tuple
-

Get ($fromip,$topip) from range attached to device, skip the mentioned ip addresses.

+

Get ($fromip,$topip) from range attached to device, skip the mentioned ip addresses.

Args

device : str
[description]
-
skip_begin : Optional[int], optional
+
skip_begin : Optional[int], optional
ips to skip from the begining of the range, Defaults to 10.
-
skip_end : Optional[int], optional
+
skip_end : Optional[int], optional
ips to skip from the end of the range, Defaults to 10.

Returns

-
tuple
+
tuple
ip range for this device
-
+
-Source code + +Expand source code +
def netrange_get(device: str, skip_begin: Optional[int] = 10, skip_end: Optional[int] = 10) -> tuple:
     """Get ($fromip,$topip) from range attached to device, skip the mentioned ip addresses.
 
@@ -1543,10 +1573,10 @@ 

Returns

-def ping_machine(ip, timeout=60, allowhostname=True) +def ping_machine(ip: str, timeout: Optional[int] = 60, allowhostname: Optional[bool] = True) ‑> bool
-

Ping a machine to check if it's up/running and accessible +

Ping a machine to check if it's up/running and accessible Note: Any well-behaved device on an LAN or WAN is free to ignore nearly any traffic, so PINGs, port scans, and the like are all unreliable.

Args

@@ -1560,18 +1590,20 @@

Args

Raises

-
ValueError
+
ValueError
if ip is Invalid ip address
-
NotImplementedError
+
NotImplementedError
if the function runs on unsupported system

Returns

-
bool
+
bool
True if machine is pingable, False otherwise
-
+
-Source code + +Expand source code +
def ping_machine(ip: str, timeout: Optional[int] = 60, allowhostname: Optional[bool] = True) -> bool:
     """Ping a machine to check if it's up/running and accessible
     Note: Any well-behaved device on an LAN or WAN is free to ignore nearly any traffic,
@@ -1612,14 +1644,18 @@ 

Returns

-def tcp_connection_test(ipaddr, port, timeout=None) +def tcp_connection_test(ipaddr: str, port: int, timeout: Optional[int] = None) ‑> bool
-

tests tcp connection on specified port, compatible with both IPv4 and IPv6. +

tests tcp connection on specified port, compatible with both IPv4 and IPv6. ensures that each side of the connection is reachable in the network.

Raises

-

socket.gaierror: raised for address-related errors. -socket.herror: raised for address-related errors.

+
+
socket.gaierror
+
raised for address-related errors.
+
socket.herror
+
raised for address-related errors.
+

Args

ipaddr : str
@@ -1631,11 +1667,13 @@

Args

Returns

-
bool
+
bool
True if the test succeeds, False otherwise
-
+
-Source code + +Expand source code +
def tcp_connection_test(ipaddr: str, port: int, timeout: Optional[int] = None) -> bool:
     """tests tcp connection on specified port, compatible with both IPv4 and IPv6.
     ensures that each side of the connection is reachable in the network.
@@ -1660,10 +1698,10 @@ 

Returns

-def udp_connection_test(ipaddr, port, timeout=1, message=b'') +def udp_connection_test(ipaddr: str, port: int, timeout: Optional[int] = 1, message: Optional[bytes] = b'') ‑> bool
-

tests udp connection on specified port by sending specified message and expecting +

tests udp connection on specified port by sending specified message and expecting to receive at least one byte from the socket as an indicator of connection success

Args

@@ -1678,16 +1716,18 @@

Args

Raises

-
ValueError
+
ValueError
raises if invalid ip address was used

Returns

-
bool
+
bool
True if the test succeeds, False otherwise
-
+
-Source code + +Expand source code +
def udp_connection_test(ipaddr: str, port: int, timeout: Optional[int] = 1, message: Optional[bytes] = b"") -> bool:
     """tests udp connection on specified port by sending specified message and expecting
     to receive at least one byte from the socket as an indicator of connection success
@@ -1724,10 +1764,10 @@ 

Returns

-def wait_connection_test(ipaddr, port, timeout=6) +def wait_connection_test(ipaddr: str, port: int, timeout: Optional[int] = 6) ‑> bool
-

Will wait until port listens on the specified address or timeout sec elapsed

+

Will wait until port listens on the specified address or timeout sec elapsed

under the hood the function will try to connect every interval sec, if waiting time timeout set to value <= 2, interval is 1 sec, otherwise 2.

Args

@@ -1742,11 +1782,13 @@

Args

Returns

-
bool
+
bool
True if the test succeeds, False otherwise
-
+
-Source code + +Expand source code +
def wait_connection_test(ipaddr: str, port: int, timeout: Optional[int] = 6) -> bool:
     """Will wait until port listens on the specified address or `timeout` sec elapsed
 
@@ -1777,10 +1819,10 @@ 

Returns

-def wait_http_test(url, timeout=60, verify=True, interval_time=2) +def wait_http_test(url: str, timeout: Optional[int] = 60, verify: Optional[bool] = True, interval_time: Optional[int] = 2) ‑> bool
-

Will keep try to reach specified url every {interval_time} sec until url become reachable or {timeout} sec elapsed

+

Will keep try to reach specified url every {interval_time} sec until url become reachable or {timeout} sec elapsed

Args

url : str
@@ -1794,16 +1836,18 @@

Args

Raises

-
ValueError
+
ValueError
raises if not correct url

Returns

-
bool
+
bool
true if the test succeeds
-
+
-Source code + +Expand source code +
def wait_http_test(
     url: str, timeout: Optional[int] = 60, verify: Optional[bool] = True, interval_time: Optional[int] = 2
 ) -> bool:
@@ -1875,9 +1919,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/sals/process/index.html b/docs/api/jumpscale/sals/process/index.html index 66ad1a23b..a4cbb3330 100644 --- a/docs/api/jumpscale/sals/process/index.html +++ b/docs/api/jumpscale/sals/process/index.html @@ -3,15 +3,17 @@ - + jumpscale.sals.process API documentation - - - - - + + + + + + +
@@ -23,91 +25,88 @@

Module jumpscale.sals.process

This module execute process on system and manage them

below are some examples of the functions included in this module (not all inclusive.):

Examples

-

```

-
-
-
-

from jumpscale.loader import j -import signal

-
-
-
+
```python-repl
+>>> from jumpscale.loader import j
+>>> import signal
+

to create a process

-
>>> rc, out, err = j.sals.process.execute("ls", cwd="/tmp", showout=True)
-# this executes ls command on dir "/tmp" showing output from stdout
+
>>> rc, out, err = j.sals.process.execute("ls", cwd="/tmp", showout=True)
+# this executes ls command on dir "/tmp" showing output from stdout
+# rc -> contains exit status
+# out -> the actual output
+# err -> in case an error happened this var will contains the error msg
 
-

rc -> contains exit status

-

out -> the actual output

-

err -> in case an error happened this var will contains the error msg

checks if a process with this pid is exists in the current process list

-
>>> j.sals.process.is_alive(10022)
+
>>> j.sals.process.is_alive(10022)
 

Checks if a specific command is available on the system

-
>>> j.sals.process.is_installed('top')
+
>>> j.sals.process.is_installed('top')
 

kill a process with pid 10022 with SIGTERM

-
>>> j.sals.process.kill(10022)
+
>>> j.sals.process.kill(10022)
 

kill a process with pid 10022 with SIGTERM, wait 3 seconds for it to disappear, then if still alive kill it with SIGKILL

-
>>> j.sals.process.kill(10022, timeout=3, sure_kill=True)
+
>>> j.sals.process.kill(10022, timeout=3, sure_kill=True)
 

Check if there is any running process that match the given name.

-
>>> j.sals.process.ps_find('python3')
+
>>> j.sals.process.ps_find('python3')
 

gets pid of the process listenning on port TCP 8000 ipv4 localhost address

-
>>> j.sals.process.get_pid_by_port(8000)
+
>>> j.sals.process.get_pid_by_port(8000)
 

gets pid of the process listenning on port UDP 8000 ipv6 localhost address

-
>>> j.sals.process.get_pid_by_port(8000, ipv6=True, udp=True)
+
>>> j.sals.process.get_pid_by_port(8000, ipv6=True, udp=True)
 

Returns the psutil.Process object that is listening on the given port

-
>>> j.sals.process.get_process_by_port(8000, ipv6=True, udp=True)
+
>>> j.sals.process.get_process_by_port(8000, ipv6=True, udp=True)
 

Get pids of process by a filter string and sort by cpu utilization descendingly

-
>>> j.sals.process.get_pids_filtered_sorted('chrome', sort='%cpu', desc=True)
+
>>> j.sals.process.get_pids_filtered_sorted('chrome', sort='%cpu', desc=True)
 

Return a list of processes ID(s) matching the given name.

-
>>> j.sals.process.get_pids('code')
+
>>> j.sals.process.get_pids('code')
 

Return a list of processes ID(s) matching the given name, including the result of matching againest the full command line.

-
>>> j.sals.process.get_pids('http.server', full_cmd_line=True)
+
>>> j.sals.process.get_pids('http.server', full_cmd_line=True)
 

Return a list of processes ID(s) matching the given name, including any zombie processes

-
>>> j.sals.process.get_pids('python3', include_zombie=False)
+
>>> j.sals.process.get_pids('python3', include_zombie=False)
 

get processes info about top 3 processes which consumed the most memory

-
>>> j.sals.process.get_processes_info(limit=3)
+
>>> j.sals.process.get_processes_info(limit=3)
 

get processes info about top 3 processes which consumed the most cpu time

-
>>> j.sals.process.get_processes_info(sort='cpu_time', limit=3)
+
>>> j.sals.process.get_processes_info(sort='cpu_time', limit=3)
 

get processes info about last process started

-
>>> j.sals.process.get_processes_info(sort='create_time', limit=1)
+
>>> j.sals.process.get_processes_info(sort='create_time', limit=1)
 

get processes info sorted by pid ascending limited to 10 processes

-
>>> j.sals.process.get_processes_info(sort='pid', limit=10, desc=False)
+
>>> j.sals.process.get_processes_info(sort='pid', limit=10, desc=False)
 

Kill a process and its children (including grandchildren) with SIGTERM and fallback to SIGKILL when needed

-
>>> j.sals.process.kill_proc_tree(20778, sure_kill=True)
+
>>> j.sals.process.kill_proc_tree(20778, sure_kill=True)
 

send SIGTERM to all processes spawned by a given process, but leave the process itself.

-
>>> j.sals.process.kill_proc_tree(20778, include_parent=False)
+
>>> j.sals.process.kill_proc_tree(20778, include_parent=False)
 

Terminate a list of processes with a given list of pids, fallback to SIGKILL after 1 seconds.

-
>>> j.sals.process.kill_all_pids([3067, 7888, 10221], timeout=1, sure_kill=True)
+
>>> j.sals.process.kill_all_pids([3067, 7888, 10221], timeout=1, sure_kill=True)
 

terminate a process that listen to a given tcp port on ipv4 address

-
>>> j.sals.process.kill_process_by_port(8000)
+
>>> j.sals.process.kill_process_by_port(8000)
 

terminate a process that listen to a given udp port on ipv6 address, fallback to SIGKILL after 3 sec

-
>>> j.sals.process.kill_process_by_port(8000, udp=True, ipv6=True, timeout=3, sure_kill=True)
+
>>> j.sals.process.kill_process_by_port(8000, udp=True, ipv6=True, timeout=3, sure_kill=True)
 

Terminate all processes owned by a given user name, fallback to SIGKILL when needed

-
>>> j.sals.process.kill_user_processes('sameh', sure_kill=True)
-```
+
>>> j.sals.process.kill_user_processes('sameh', sure_kill=True)
 
+

```

-Source code + +Expand source code +
"""This module execute process on system and manage them
 
 below are some examples of the functions included in this module (not all inclusive.):
@@ -1159,7 +1158,7 @@ 

Functions

def check_process_for_pid(pid, process_name)
-

Check whether a given pid actually does belong to a given process name.

+

Check whether a given pid actually does belong to a given process name.

Args

pid : int
@@ -1169,11 +1168,13 @@

Args

Returns

-
bool
+
bool
True if process_name matched process name of the pid, False otherwise.
-
+
-Source code + +Expand source code +
def check_process_for_pid(pid, process_name):
     """Check whether a given pid actually does belong to a given process name.
 
@@ -1195,7 +1196,7 @@ 

Returns

def check_running(process_name, min=1)
-

Check if there are a specific number of running processes that match the given name.

+

Check if there are a specific number of running processes that match the given name.

Function will check string against Process.name(), Process.exe() and Process.cmdline().

Args

@@ -1206,11 +1207,13 @@

Args

Returns

-
bool
+
bool
true if process is running, otherwise False
-
+
-Source code + +Expand source code +
def check_running(process_name, min=1):
     """Check if there are a specific number of running processes that match the given name.
 
@@ -1231,7 +1234,7 @@ 

Returns

def check_start(cmd, filterstr, n_instances=1, retry=1, timeout=2, delay=0.5)
-

Run command (possibly multiple times) and check if it is started based on filterstr

+

Run command (possibly multiple times) and check if it is started based on filterstr

Args

cmd : str or list of str
@@ -1248,9 +1251,14 @@

Args

how long the function should delay the checking process after the process finished or timeout exceeded (in case the first process started another one).

Raises

-

j.exceptions.Runtime: will be raised if we didn't reach number of required instances.

+
+
j.exceptions.Runtime
+
will be raised if we didn't reach number of required instances.
+
-Source code + +Expand source code +
def check_start(cmd, filterstr, n_instances=1, retry=1, timeout=2, delay=0.5):
     """Run command (possibly multiple times) and check if it is started based on filterstr
 
@@ -1294,7 +1302,7 @@ 

Raises

def check_stop(cmd, filterstr, retry=1, n_instances=0, timeout=2, delay=0.5)
-

Executes a stop command (possibly multiple times) and check if it is already stopped based on filterstr

+

Executes a stop command (possibly multiple times) and check if it is already stopped based on filterstr

Args

cmd : str
@@ -1312,11 +1320,13 @@

Args

Raises

-
j.exceptions.Runtime: if number of instances not matched
-
 
-
+
j.exceptions.Runtime
+
if number of instances not matched
+
-Source code + +Expand source code +
def check_stop(cmd, filterstr, retry=1, n_instances=0, timeout=2, delay=0.5):
     """Executes a stop command (possibly multiple times) and check if it is already stopped based on filterstr
 
@@ -1361,10 +1371,10 @@ 

Raises

-def execute(cmd, showout=False, cwd=None, shell='/bin/bash', timeout=600, asynchronous=False, env=None, replace_env=False, die=False) +def execute(cmd, showout=False, cwd=None, shell='/bin/bash', timeout=600, asynchronous=False, env=None, replace_env=False, die=False)
-

Execute a command.

+

Execute a command.

Accepts command as a list too, with auto-escaping.

Args

@@ -1389,11 +1399,13 @@

Args

Returns

-
tuple
+
tuple
tuple[return_code: int, stdout: str, stderr: str]
-
+
-Source code + +Expand source code +
def execute(
     cmd,
     showout=False,
@@ -1440,11 +1452,16 @@ 

Returns

def get_defunct_processes()
-

Gets defunct (zombie) processes.

+

Gets defunct (zombie) processes.

Returns

-

list of int: List of processes ID(s).

+
+
list of int
+
List of processes ID(s).
+
-Source code + +Expand source code +
def get_defunct_processes():
     """Gets defunct (zombie) processes.
 
@@ -1465,22 +1482,28 @@ 

Returns

def get_environ(pid)
-

Gets env vars for a specific process based on pid

+

Gets env vars for a specific process based on pid

Args

pid : int
process pid

Raises

-

j.exceptions.NotFound: if the process is no longer exists. -j.exceptions.Permission: if the process is not accessible by the user.

+
+
j.exceptions.NotFound
+
if the process is no longer exists.
+
j.exceptions.Permission
+
if the process is not accessible by the user.
+

Returns

-
dict
+
dict
dict of env variables
-
+
-Source code + +Expand source code +
def get_environ(pid):
     """Gets env vars for a specific process based on pid
 
@@ -1507,21 +1530,23 @@ 

Returns

def get_filtered_pids(filterstr, excludes=None)
-

Get pids filtered by filterstr and excludes, matching against the full command line used to start the process.

+

Get pids filtered by filterstr and excludes, matching against the full command line used to start the process.

Args

filterstr : str
the String to filter based on.
-
excludes : list[str]
+
excludes : list[str]
exclude list. Defaults to None.

Returns

-
list of int: List of the processes IDs
-
 
-
+
list of int
+
List of the processes IDs
+
-Source code + +Expand source code +
def get_filtered_pids(filterstr, excludes=None):
     """Get pids filtered by filterstr and excludes, matching against the full command line used to start the process.
 
@@ -1555,18 +1580,20 @@ 

Returns

def get_memory_usage()
-

Get memory status

+

Get memory status

Returns

-
dict
+
dict
Memory status info, available keys ('total', 'used', 'percent') 'total': total physical memory in Gb (exclusive swap). 'used': memory used in Gb, calculated differently depending on the platform and designed for informational purposes only. total - free does not necessarily match used. 'percent': the percentage of used memory.
-
+
-Source code + +Expand source code +
def get_memory_usage():
     """Get memory status
 
@@ -1591,11 +1618,16 @@ 

Returns

def get_my_process()
-

Get psutil.Process object of the current process.

+

Get psutil.Process object of the current process.

Returns

-

psutil.Process: Process object of the current process.

+
+
psutil.Process
+
Process object of the current process.
+
-Source code + +Expand source code +
def get_my_process():
     """Get psutil.Process object of the current process.
 
@@ -1609,7 +1641,7 @@ 

Returns

def get_pid_by_port(port, ipv6=False, udp=False)
-

Returns the PID of the process that is listening on the given port

+

Returns the PID of the process that is listening on the given port

Args

port : int
@@ -1620,9 +1652,14 @@

Args

Whether to search the connections for UDP port instead of TCP. Defaults to False.

Returns

-

int or None: PID for the proceses that listen on that port.

+
+
int or None
+
PID for the proceses that listen on that port.
+
-Source code + +Expand source code +
def get_pid_by_port(port, ipv6=False, udp=False):
     """Returns the PID of the process that is listening on the given port
 
@@ -1644,7 +1681,7 @@ 

Returns

def get_pids(process_name, match_predicate=None, limit=0, include_zombie=False, full_cmd_line=False)
-

Return a list of processes ID(s) matching a given process name.

+

Return a list of processes ID(s) matching a given process name.

Function will check string against Process.name(), Process.exe() and Process.cmdline()

Args

@@ -1665,9 +1702,14 @@

Args

if full_cmd_line is set to True, the full command line is used. Defaults to False.

Returns

-

list of int: List of the processes IDs.

+
+
list of int
+
List of the processes IDs.
+
-Source code + +Expand source code +
def get_pids(process_name, match_predicate=None, limit=0, _alt_source=None, include_zombie=False, full_cmd_line=False):
     """Return a list of processes ID(s) matching a given process name.
 
@@ -1730,16 +1772,21 @@ 

Returns

def get_pids_filtered_by_regex(regex_list)
-

Get pids of a process filtered by Regex list, matching against the full command line used to start the process.

+

Get pids of a process filtered by Regex list, matching against the full command line used to start the process.

Args

-
regex_list : list[str]
+
regex_list : list[str]
List of regex expressions.

Returns

-

list of int: List of the processes IDs.

+
+
list of int
+
List of the processes IDs.
+
-Source code + +Expand source code +
def get_pids_filtered_by_regex(regex_list):
     """Get pids of a process filtered by Regex list, matching against the full command line used to start the process.
 
@@ -1763,7 +1810,7 @@ 

Returns

def get_pids_filtered_sorted(filterstr, sortkey=None, desc=False)
-

Get pids of process by a filter string and optionally sort by sortkey

+

Get pids of process by a filter string and optionally sort by sortkey

Args

filterstr : str
@@ -1804,11 +1851,13 @@

Args

Returns

-
list of int: list of the processes IDs
-
 
-
+
list of int
+
list of the processes IDs
+
-Source code + +Expand source code +
def get_pids_filtered_sorted(filterstr, sortkey=None, desc=False):
     """Get pids of process by a filter string and optionally sort by sortkey
 
@@ -1854,17 +1903,17 @@ 

Returns

-def get_ports_mapping(status='LISTEN') +def get_ports_mapping(status='LISTEN')
-

Get a mapping for process to ports with a status filter

+

Get a mapping for process to ports with a status filter

It will skip any process in case of errors (e.g. permission error)

Example

-
>>> from jumpscale.loader import j
+
>>> from jumpscale.loader import j
 >>> import psutil
 >>> j.sals.process.get_ports_mapping(psutil.CONN_ESTABLISHED)
 >>> # or
->>> j.sals.process.get_ports_mapping("ESTABLISHED")
+>>> j.sals.process.get_ports_mapping("ESTABLISHED")
 

Args

@@ -1873,11 +1922,13 @@

Args

Returns

-
defaultdict
+
defaultdict
a mapping between process and ports
-
+
-Source code + +Expand source code +
def get_ports_mapping(status=psutil.CONN_LISTEN):
     """Get a mapping for process to ports with a status filter
 
@@ -1916,7 +1967,7 @@ 

Returns

def get_process_by_port(port, ipv6=False, udp=False)
-

Returns the psutil.Process object that is listening on the given port.

+

Returns the psutil.Process object that is listening on the given port.

Args

port : int
@@ -1927,16 +1978,23 @@

Args

Whether to search the connections for UDP port instead of TCP. Defaults to False.

Raises

-

j.exceptions.Runtime: pid is not retrievable. -j.exceptions.NotFound: if the process is no longer exists. -j.exceptions.Permission: if the process is not accessible by the user.

+
+
j.exceptions.Runtime
+
pid is not retrievable.
+
j.exceptions.NotFound
+
if the process is no longer exists.
+
j.exceptions.Permission
+
if the process is not accessible by the user.
+

Returns

-
psutil.Process: process object if found, otherwise None
-
 
-
+
psutil.Process
+
process object if found, otherwise None
+
-Source code + +Expand source code +
def get_process_by_port(port, ipv6=False, udp=False):
     """Returns the psutil.Process object that is listening on the given port.
 
@@ -1977,7 +2035,7 @@ 

Returns

def get_process_object(pid, die=False)
-

Get psutil.Process object of a given process ID (PID).

+

Get psutil.Process object of a given process ID (PID).

Args

pid : int
@@ -1987,12 +2045,21 @@

Args

current process list or not. Defaults to False.

Raises

-

psutil.NoSuchProcess: If process with the given PID is not found and die set to True. -psutil.AccessDenied: If permission denied.

+
+
psutil.NoSuchProcess
+
If process with the given PID is not found and die set to True.
+
psutil.AccessDenied
+
If permission denied.
+

Returns

-

psutil.Process or None: The Process object of the given PID if found, otherwise None, if die set to False.

+
+
psutil.Process or None
+
The Process object of the given PID if found, otherwise None, if die set to False.
+
-Source code + +Expand source code +
def get_process_object(pid, die=False):
     """Get psutil.Process object of a given process ID (PID).
 
@@ -2022,14 +2089,16 @@ 

Returns

def get_processes()
-

Get an interator for all running processes

+

Get an interator for all running processes

Yields

-
psutil.Process: for all processes running
-
 
-
+
psutil.Process
+
for all processes running
+
-Source code + +Expand source code +
def get_processes():
     """Get an interator for all running processes
 
@@ -2040,13 +2109,13 @@ 

Yields

-def get_processes_info(user=None, sort='mem', filterstr=None, limit=25, desc=True) +def get_processes_info(user=None, sort='mem', filterstr=None, limit=25, desc=True)
-

Get information for top running processes sorted by memory usage or CPU usage.

+

Get information for top running processes sorted by memory usage or CPU usage.

Args

-
user : [type], optional
+
user : [type], optional
filter the processes by username. Defaults to None.
sort : str, optional
sort processes by resource usage, Defaults to 'mem'. @@ -2074,7 +2143,7 @@

Args

Returns

-
dict
+
dict
processes info as a dictionary available keys [ "cpu_num", @@ -2093,9 +2162,11 @@

Returns

"cpu_time", "ports" ]
-
+
-Source code + +Expand source code +
def get_processes_info(user=None, sort="mem", filterstr=None, limit=25, desc=True):
     """Get information for top running processes sorted by memory usage or CPU usage.
 
@@ -2220,7 +2291,7 @@ 

Returns

def get_similar_processes(target_proc=None)
-

Gets similar processes to current process, started with same command line and same options.

+

Gets similar processes to current process, started with same command line and same options.

Args

target_proc : int or psutil.Process, optional
@@ -2228,9 +2299,14 @@

Args

if None then pid for current process will be used. Defaults to None.

Yields

-

psutil.Process: psutil.Process object for all processes similar to a given process.

+
+
psutil.Process
+
psutil.Process object for all processes similar to a given process.
+
-Source code + +Expand source code +
def get_similar_processes(target_proc=None):
     """Gets similar processes to current process, started with same command line and same options.
 
@@ -2257,16 +2333,21 @@ 

Yields

def get_user_processes(user)
-

Get all process for a specific user.

+

Get all process for a specific user.

Args

user : str
The user name to match against.

Yields

-

psutil.Process: process object for all processes owned by user.

+
+
psutil.Process
+
process object for all processes owned by user.
+
-Source code + +Expand source code +
def get_user_processes(user):
     """Get all process for a specific user.
 
@@ -2288,14 +2369,16 @@ 

Yields

def in_docker()
-

will check if we are in a docker.

+

will check if we are in a docker.

Returns

-
bool
+
bool
True if in docker. False otherwise.
-
+
-Source code + +Expand source code +
def in_docker():
     """will check if we are in a docker.
 
@@ -2310,14 +2393,16 @@ 

Returns

def in_host()
-

Will check if we are in a host.

+

Will check if we are in a host.

Returns

-
bool
+
bool
True if in host. False otherwise.
-
+
-Source code + +Expand source code +
def in_host():
     """Will check if we are in a host.
 
@@ -2331,7 +2416,7 @@ 

Returns

def is_alive(pid)
-

Check whether the given PID exists in the current process list.

+

Check whether the given PID exists in the current process list.

Args

pid : int
@@ -2339,11 +2424,13 @@

Args

Returns

-
bool
+
bool
True if the given PID exists in the current process list, False otherwise.
-
+
-Source code + +Expand source code +
def is_alive(pid):
     """Check whether the given PID exists in the current process list.
 
@@ -2360,7 +2447,7 @@ 

Returns

def is_installed(cmd)
-

Checks if a specific command is available on system e.g. curl.

+

Checks if a specific command is available on system e.g. curl.

Args

cmd : str
@@ -2368,11 +2455,13 @@

Args

Returns

-
bool
+
bool
True if command is available, False otherwise.
-
+
-Source code + +Expand source code +
def is_installed(cmd):
     """Checks if a specific command is available on system e.g. curl.
 
@@ -2390,7 +2479,7 @@ 

Returns

def is_port_listening(port, ipv6=False)
-

Check if the TCP port is being used by any process

+

Check if the TCP port is being used by any process

Args

port : int
@@ -2400,11 +2489,13 @@

Args

Returns

-
bool
+
bool
True if port is used, False otherwise.
-
+
-Source code + +Expand source code +
def is_port_listening(port, ipv6=False):
     """Check if the TCP port is being used by any process
 
@@ -2423,10 +2514,10 @@ 

Returns

-def kill(proc, sig=<Signals.SIGTERM: 15>, timeout=5, sure_kill=False) +def kill(proc, sig=Signals.SIGTERM, timeout=5, sure_kill=False)
-

Kill a process with a specified signal.

+

Kill a process with a specified signal.

Args

proc : int or psutil.Process
@@ -2440,10 +2531,16 @@

Args

Whether to fallback to SIGKILL if the timeout exceeded for the terminate operation or not. Defaults to False.

Raises

-

j.exceptions.Runtime: In case killing the process failed. -j.exceptions.Permission: In case the permission to perform this action is denied.

+
+
j.exceptions.Runtime
+
In case killing the process failed.
+
j.exceptions.Permission
+
In case the permission to perform this action is denied.
+
-Source code + +Expand source code +
def kill(proc, sig=signal.SIGTERM, timeout=5, sure_kill=False):
     """Kill a process with a specified signal.
 
@@ -2497,10 +2594,10 @@ 

Raises

-def kill_all_pids(pids, sig=<Signals.SIGTERM: 15>, timeout=5, sure_kill=False) +def kill_all_pids(pids, sig=Signals.SIGTERM, timeout=5, sure_kill=False)
-

Kill all processes with given pids.

+

Kill all processes with given pids.

Args

pids : list of int
@@ -2514,9 +2611,14 @@

Args

Whether to fallback to SIGKILL if the timeout exceeded for the terminate operation or not. Defaults to False.

Returns

-

list of int: represents the IDs of the processes remaning alive if any.

+
+
list of int
+
represents the IDs of the processes remaning alive if any.
+
-Source code + +Expand source code +
def kill_all_pids(pids, sig=signal.SIGTERM, timeout=5, sure_kill=False):
     """Kill all processes with given pids.
 
@@ -2541,10 +2643,10 @@ 

Returns

-def kill_proc_tree(parent, sig=<Signals.SIGTERM: 15>, include_parent=True, include_grand_children=True, timeout=5, sure_kill=False) +def kill_proc_tree(parent, sig=Signals.SIGTERM, include_parent=True, include_grand_children=True, timeout=5, sure_kill=False)
-

Kill a process and its children (including grandchildren) with signal sig

+

Kill a process and its children (including grandchildren) with signal sig

Args

proc : int or psutil.Process
@@ -2560,14 +2662,19 @@

Args

Whether to fallback to SIGKILL if the timeout exceeded for the terminate operation or not. Defaults to False.

Returns

-

list of psutil.Process: represents the objects of the processes remaning alive if any.

+
+
list of psutil.Process
+
represents the objects of the processes remaning alive if any.
+

Raises

-
AssertionError
+
AssertionError
in case the given parent is the current process
-
+
-Source code + +Expand source code +
def kill_proc_tree(
     parent, sig=signal.SIGTERM, include_parent=True, include_grand_children=True, timeout=5, sure_kill=False
 ):
@@ -2616,10 +2723,10 @@ 

Raises

-def kill_process_by_name(process_name, sig=<Signals.SIGTERM: 15>, match_predicate=None, timeout=5, sure_kill=False) +def kill_process_by_name(process_name, sig=Signals.SIGTERM, match_predicate=None, timeout=5, sure_kill=False)
-

Kill all processes that match 'process_name'.

+

Kill all processes that match 'process_name'.

Args

process_name : str
@@ -2637,9 +2744,14 @@

Args

Whether to fallback to SIGKILL if the timeout exceeded for the terminate operation or not. Defaults to False.

Returns

-

list of int: represents the IDs of the processes remaning alive if any.

+
+
list of int
+
represents the IDs of the processes remaning alive if any.
+
-Source code + +Expand source code +
def kill_process_by_name(process_name, sig=signal.SIGTERM, match_predicate=None, timeout=5, sure_kill=False):
     """Kill all processes that match 'process_name'.
 
@@ -2667,10 +2779,10 @@ 

Returns

-def kill_process_by_port(port, ipv6=False, udp=False, sig=<Signals.SIGTERM: 15>, timeout=5, sure_kill=False) +def kill_process_by_port(port, ipv6=False, udp=False, sig=Signals.SIGTERM, timeout=5, sure_kill=False)
-

Kill process by port.

+

Kill process by port.

Args

port : int
@@ -2688,10 +2800,16 @@

Args

Whether to fallback to SIGKILL if the timeout exceeded for the terminate operation or not. Defaults to False.

Raises

-

j.exceptions.Runtime: In case killing the process failed. -j.exceptions.Permission: In case the permission to perform this action is denied.

+
+
j.exceptions.Runtime
+
In case killing the process failed.
+
j.exceptions.Permission
+
In case the permission to perform this action is denied.
+
-Source code + +Expand source code +
def kill_process_by_port(port, ipv6=False, udp=False, sig=signal.SIGTERM, timeout=5, sure_kill=False):
     """Kill process by port.
 
@@ -2713,10 +2831,10 @@ 

Raises

-def kill_user_processes(user, sig=<Signals.SIGTERM: 15>, timeout=5, sure_kill=False) +def kill_user_processes(user, sig=Signals.SIGTERM, timeout=5, sure_kill=False)
-

Kill all processes for a specific user.

+

Kill all processes for a specific user.

Args

user : str
@@ -2729,9 +2847,11 @@

Args

Whether to fallback to SIGKILL if the timeout exceeded for the terminate operation or not. Defaults to False.

Returns

-

list of psutil.Process): list of process objects that remain alive if any.

+

list of psutil.Process): list of process objects that remain alive if any.

-Source code + +Expand source code +
def kill_user_processes(user, sig=signal.SIGTERM, timeout=5, sure_kill=False):
     """Kill all processes for a specific user.
 
@@ -2763,7 +2883,7 @@ 

Returns

def ps_find(process_name)
-

Check if there is any running process that match the given name.

+

Check if there is any running process that match the given name.

Args

process_name : str
@@ -2771,11 +2891,13 @@

Args

Returns

-
bool
+
bool
True if process is found, False otherwise.
-
+
-Source code + +Expand source code +
def ps_find(process_name):
     """Check if there is any running process that match the given name.
 
@@ -2792,7 +2914,7 @@ 

Returns

def set_env_var(var_names, var_values)
-

Set the value of the environment variables {varnames}. Existing variable are overwritten

+

Set the value of the environment variables {varnames}. Existing variable are overwritten

Such changes to the environment affect subprocesses started with os.system(), popen() or fork() and execv()

Args

@@ -2803,11 +2925,13 @@

Args

Raises

-
j.exceptions.RuntimeError: if error happened during setting the environment variables
-
 
-
+
j.exceptions.RuntimeError
+
if error happened during setting the environment variables
+
-Source code + +Expand source code +
def set_env_var(var_names, var_values):
     """Set the value of the environment variables {varnames}. Existing variable are overwritten
 
@@ -2886,9 +3010,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/sals/testdocs/index.html b/docs/api/jumpscale/sals/testdocs/index.html index 4afb685fe..ec25dd209 100644 --- a/docs/api/jumpscale/sals/testdocs/index.html +++ b/docs/api/jumpscale/sals/testdocs/index.html @@ -3,15 +3,17 @@ - + jumpscale.sals.testdocs API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.sals.testdocs

-Source code + +Expand source code +
import pytest
 from jumpscale.loader import j
 
@@ -177,7 +181,7 @@ 

Functions

def generate_tests_docs(source, target, clean=False)
-

Generate a markdown docs for tests from its docstring.

+

Generate a markdown docs for tests from its docstring.

Args

source : str
@@ -186,9 +190,11 @@

Args

Target docs path.
clean : bool
To clean the target path before start.
-
+
-Source code + +Expand source code +
def generate_tests_docs(source, target, clean=False):
     """Generate a markdown docs for tests from its docstring.
 
@@ -216,9 +222,11 @@ 

Classes

(source, target)
-
+
-Source code + +Expand source code +
class Collector:
     def __init__(self, source, target):
         self.tests_docs_locations = []
@@ -334,14 +342,16 @@ 

Methods

def pytest_collection_modifyitems(self, items)
-

This method is a pytest hook which can modify the items has been collected.

+

This method is a pytest hook which can modify the items has been collected.

Args

items : list
Tests objects has been collected.
-
+
-Source code + +Expand source code +
def pytest_collection_modifyitems(self, items):
     """This method is a pytest hook which can modify the items has been collected.
 
@@ -387,9 +397,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/sals/unix/index.html b/docs/api/jumpscale/sals/unix/index.html index 70ef0839a..ab45ce610 100644 --- a/docs/api/jumpscale/sals/unix/index.html +++ b/docs/api/jumpscale/sals/unix/index.html @@ -3,15 +3,17 @@ - + jumpscale.sals.unix API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.sals.unix

-Source code + +Expand source code +
from .user import *
@@ -30,7 +34,7 @@

Sub-modules

jumpscale.sals.unix.user
-
+

@@ -61,9 +65,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/sals/unix/user.html b/docs/api/jumpscale/sals/unix/user.html index da1257d78..e6f37e8ec 100644 --- a/docs/api/jumpscale/sals/unix/user.html +++ b/docs/api/jumpscale/sals/unix/user.html @@ -3,15 +3,17 @@ - + jumpscale.sals.unix.user API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.sals.unix.user

-Source code + +Expand source code +
import os, pwd, grp
 
 __all__ = ["get_user_pwd", "get_current_pwd", "get_group_grp", "get_current_grp"]
@@ -78,14 +82,13 @@ 

Functions

def get_current_grp()
-

get current group info

+

get current group info

Returns

-
-
grp.struct_group
-
 
-
+

grp.struct_group

-Source code + +Expand source code +
def get_current_grp():
     """get current group info
 
@@ -99,14 +102,13 @@ 

Returns

def get_current_pwd()
-

get current user passwd record

+

get current user passwd record

Returns

-
-
pwd.struct_passwd
-
 
-
+

pwd.struct_passwd

-Source code + +Expand source code +
def get_current_pwd():
     """get current user passwd record
 
@@ -120,19 +122,18 @@ 

Returns

def get_group_grp(gid)
-

get group info

+

get group info

Args

gid : int
gid of the group

Returns

-
-
grp.struct_group
-
 
-
+

grp.struct_group

-Source code + +Expand source code +
def get_group_grp(gid):
     """get group info
     Args:
@@ -148,19 +149,18 @@ 

Returns

def get_user_pwd(uid)
-

get user passwd record

+

get user passwd record

Args

uid : int
uid of the user

Returns

-
-
pwd.struct_passwd
-
 
-
+

pwd.struct_passwd

-Source code + +Expand source code +
def get_user_pwd(uid):
     """get user passwd record
     Args:
@@ -200,9 +200,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/servers/index.html b/docs/api/jumpscale/servers/index.html index 73dcf60a8..714d4ee17 100644 --- a/docs/api/jumpscale/servers/index.html +++ b/docs/api/jumpscale/servers/index.html @@ -3,15 +3,17 @@ - + jumpscale.servers API documentation - - - - - + + + + + + +
@@ -26,11 +28,11 @@

Sub-modules

jumpscale.servers.openresty
-
+
jumpscale.servers.rack
-
+
@@ -62,9 +64,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/servers/openresty/index.html b/docs/api/jumpscale/servers/openresty/index.html index 5a97f1349..b58315462 100644 --- a/docs/api/jumpscale/servers/openresty/index.html +++ b/docs/api/jumpscale/servers/openresty/index.html @@ -3,15 +3,17 @@ - + jumpscale.servers.openresty API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.servers.openresty

-Source code + +Expand source code +
def export_module_as():
     from jumpscale.core.base import StoredFactory
     from .server import OpenRestyServer
@@ -34,15 +38,15 @@ 

Sub-modules

jumpscale.servers.openresty.location
-
+
jumpscale.servers.openresty.server
-
+
jumpscale.servers.openresty.utils
-
+
@@ -55,9 +59,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
     from jumpscale.core.base import StoredFactory
     from .server import OpenRestyServer
@@ -97,9 +103,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/servers/openresty/location.html b/docs/api/jumpscale/servers/openresty/location.html index 438110a02..723dfd515 100644 --- a/docs/api/jumpscale/servers/openresty/location.html +++ b/docs/api/jumpscale/servers/openresty/location.html @@ -3,15 +3,17 @@ - + jumpscale.servers.openresty.location API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.servers.openresty.location

-Source code + +Expand source code +
from jumpscale.loader import j
 from jumpscale.core.base import Base, fields
 from .utils import render_config_template
@@ -89,11 +93,11 @@ 

Classes

(parent_=None, instance_name_=None, **values)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -108,9 +112,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class Location(Base):
     name = fields.String()
     path_url = fields.String()
@@ -167,16 +173,18 @@ 

Instance variables

var config
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -192,16 +200,18 @@ 

Returns

var connection_type
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -217,16 +227,18 @@ 

Returns

var force_https
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -242,16 +254,18 @@ 

Returns

var index
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -267,16 +281,18 @@ 

Returns

var ipaddr_dest
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -292,16 +308,18 @@ 

Returns

var is_auth
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -317,16 +335,18 @@ 

Returns

var location_type
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -342,16 +362,18 @@ 

Returns

var name
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -367,9 +389,11 @@ 

Returns

var path_cfg
-
+
-Source code + +Expand source code +
@property
 def path_cfg(self):
     return f"{self.path_cfg_dir}/{self.instance_name}.conf"
@@ -377,9 +401,11 @@

Returns

var path_cfg_dir
-
+
-Source code + +Expand source code +
@property
 def path_cfg_dir(self):
     return f"{self.parent.path_cfg_dir}/{self.parent.instance_name}_locations"
@@ -387,16 +413,18 @@

Returns

var path_dest
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -412,16 +440,18 @@ 

Returns

var path_location
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -437,16 +467,18 @@ 

Returns

var path_url
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -462,9 +494,11 @@ 

Returns

var path_web
-
+
-Source code + +Expand source code +
@property
 def path_web(self):
     return self.parent.path_web
@@ -472,16 +506,18 @@

Returns

var port_dest
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -497,16 +533,18 @@ 

Returns

var scheme
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -522,16 +560,18 @@ 

Returns

var use_weblibs
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -552,9 +592,11 @@ 

Methods

def configure(self)
-

Config is a server config file of nginx (in text format)

+

Config is a server config file of nginx (in text format)

-Source code + +Expand source code +
def configure(self):
     """Config is a server config file of nginx (in text format)
     """
@@ -571,12 +613,14 @@ 

Methods

-def write_config(self, content='') +def write_config(self, content='')
-
+
-Source code + +Expand source code +
def write_config(self, content=""):
     if not content:
         content = render_config_template(f"location_{self.location_type}", obj=self)
@@ -641,9 +685,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/servers/openresty/server.html b/docs/api/jumpscale/servers/openresty/server.html index 86d1e7e43..ef967bad8 100644 --- a/docs/api/jumpscale/servers/openresty/server.html +++ b/docs/api/jumpscale/servers/openresty/server.html @@ -3,15 +3,17 @@ - + jumpscale.servers.openresty.server API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.servers.openresty.server

-Source code + +Expand source code +
from jumpscale.loader import j
 from enum import Enum
 from jumpscale.core.base import Base, fields
@@ -199,11 +203,11 @@ 

Classes

(**kwargs)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -218,9 +222,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class OpenRestyServer(Base):
     status = fields.Enum(Status)
     websites = fields.Factory(Website)
@@ -347,9 +353,11 @@ 

Instance variables

var logs_dir
-
+
-Source code + +Expand source code +
@property
 def logs_dir(self):
     if not self._logs_dir:
@@ -360,9 +368,11 @@ 

Instance variables

var path_cfg
-
+
-Source code + +Expand source code +
@property
 def path_cfg(self):
     return j.sals.fs.join_paths(self.path_cfg_dir, "nginx.conf")
@@ -370,9 +380,11 @@

Instance variables

var path_cfg_dir
-
+
-Source code + +Expand source code +
@property
 def path_cfg_dir(self):
     if not self._path_cfg_dir:
@@ -383,9 +395,11 @@ 

Instance variables

var path_web
-
+
-Source code + +Expand source code +
@property
 def path_web(self):
     if not self._path_web:
@@ -396,9 +410,11 @@ 

Instance variables

var startup_cmd
-
+
-Source code + +Expand source code +
@property
 def startup_cmd(self):
     pass
@@ -406,16 +422,18 @@

Instance variables

var status
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -431,16 +449,18 @@ 

Returns

var websites
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -461,9 +481,11 @@ 

Methods

def cleanup(self)
-
+
-Source code + +Expand source code +
def cleanup(self):
     j.sals.fs.rmtree(f"{self.path_cfg_dir}/servers")
@@ -472,9 +494,11 @@

Methods

def configure(self)
-
+
-Source code + +Expand source code +
def configure(self):
     # clean old websites config
     self.cleanup()
@@ -491,7 +515,7 @@ 

Methods

def get_from_port(self, port, domain=None, ssl=None)
-

will try to get a website listening on port, if it doesn't exist it will create one

+

will try to get a website listening on port, if it doesn't exist it will create one

Args

port : int
@@ -503,11 +527,13 @@

Args

Returns

-
Website
+
Website
A new or an old website instance with the needed port
-
+
-Source code + +Expand source code +
def get_from_port(self, port, domain=None, ssl=None):
     """will try to get a website listening on port, if it doesn't exist it will create one
 
@@ -539,14 +565,16 @@ 

Returns

def install(self, reset=False)
-

Install required deps for openresty

+

Install required deps for openresty

Args

reset : bool, optional
If true will redo the installation. Defaults to False.
-
+
-Source code + +Expand source code +
def install(self, reset=False):
     """Install required deps for openresty
 
@@ -574,9 +602,11 @@ 

Args

def is_running(self)
-
+
-Source code + +Expand source code +
def is_running(self):
     pass
@@ -585,9 +615,11 @@

Args

def reload(self)
-
+
-Source code + +Expand source code +
def reload(self):
     self.configure()
     j.sals.process.execute("lapis build", cwd=self.path_cfg_dir)
@@ -597,9 +629,11 @@

Args

def start(self, reset=False)
-
+
-Source code + +Expand source code +
def start(self, reset=False):
     pass
@@ -608,9 +642,11 @@

Args

def stop(self)
-
+
-Source code + +Expand source code +
def stop(self):
     pass
@@ -629,12 +665,14 @@

Inherited members

class Status -(*args, **kwargs) +(value, names=None, *, module=None, qualname=None, type=None, start=1)
-

An enumeration.

+

An enumeration.

-Source code + +Expand source code +
class Status(Enum):
     INIT = "init"
     INSTALLED = "installed"
@@ -647,11 +685,11 @@

Class variables

var INIT
-
+
var INSTALLED
-
+
@@ -660,11 +698,11 @@

Class variables

(parent_=None, instance_name_=None, **values)
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -679,9 +717,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class Website(Base):
 
     port = fields.Integer(default=80)
@@ -723,16 +763,18 @@ 

Instance variables

var domain
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -748,16 +790,18 @@ 

Returns

var locations
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -773,16 +817,18 @@ 

Returns

var path
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -798,9 +844,11 @@ 

Returns

var path_cfg
-
+
-Source code + +Expand source code +
@property
 def path_cfg(self):
     return f"{self.path_cfg_dir}/{self.instance_name}.http.conf"
@@ -808,9 +856,11 @@

Returns

var path_cfg_dir
-
+
-Source code + +Expand source code +
@property
 def path_cfg_dir(self):
     return f"{self.parent.path_cfg_dir}/servers"
@@ -818,9 +868,11 @@

Returns

var path_web
-
+
-Source code + +Expand source code +
@property
 def path_web(self):
     return self.parent.path_web
@@ -828,16 +880,18 @@

Returns

var port
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -853,16 +907,18 @@ 

Returns

var ssl
-

getter method this property

+

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

-
any
+
any
the field value
-
+
-Source code + +Expand source code +
def getter(self):
     """
     getter method this property
@@ -883,9 +939,11 @@ 

Methods

def configure(self)
-

Writes configuration of the website and its locations

+

Writes configuration of the website and its locations

-Source code + +Expand source code +
def configure(self):
     """Writes configuration of the website and its locations
     """
@@ -974,9 +1032,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/servers/openresty/utils.html b/docs/api/jumpscale/servers/openresty/utils.html index 62b6e55d1..b7f297d24 100644 --- a/docs/api/jumpscale/servers/openresty/utils.html +++ b/docs/api/jumpscale/servers/openresty/utils.html @@ -3,15 +3,17 @@ - + jumpscale.servers.openresty.utils API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.servers.openresty.utils

-Source code + +Expand source code +
from jumpscale.loader import j
 
 
@@ -44,9 +48,11 @@ 

Functions

def render_config_template(name, **kwargs)
-
+
-Source code + +Expand source code +
def render_config_template(name, **kwargs):
     return env.get_template(f"{name}.conf").render(**kwargs)
@@ -76,9 +82,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/servers/rack/index.html b/docs/api/jumpscale/servers/rack/index.html index baf9b9eb7..580003f9f 100644 --- a/docs/api/jumpscale/servers/rack/index.html +++ b/docs/api/jumpscale/servers/rack/index.html @@ -3,15 +3,17 @@ - + jumpscale.servers.rack API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.servers.rack

-Source code + +Expand source code +
def export_module_as():
     from .rack import ServerRack
     return ServerRack()
@@ -32,7 +36,7 @@

Sub-modules

jumpscale.servers.rack.rack
-
+
@@ -45,9 +49,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
     from .rack import ServerRack
     return ServerRack()
@@ -83,9 +89,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/servers/rack/rack.html b/docs/api/jumpscale/servers/rack/rack.html index 450c27e8b..76d2fed4a 100644 --- a/docs/api/jumpscale/servers/rack/rack.html +++ b/docs/api/jumpscale/servers/rack/rack.html @@ -3,15 +3,17 @@ - + jumpscale.servers.rack.rack API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.servers.rack.rack

-Source code + +Expand source code +
from gevent import event
 
 from jumpscale.core.base import Base
@@ -119,11 +123,11 @@ 

Classes

class ServerRack
-

A simple attribute-based namespace.

+

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

-
class Person(Base):
+
class Person(Base):
     name = fields.String()
     age = fields.Float()
 
@@ -138,9 +142,11 @@ 

Args

instance name. Defaults to None.
**values
any given field values to initiate the instance with
-
+
-Source code + +Expand source code +
class ServerRack(Base):
     def __init__(self):
         super().__init__()
@@ -230,16 +236,18 @@ 

Methods

def add(self, server_name, server)
-

Add new server to the rack

+

Add new server to the rack

Args

name : str
server name
server : object
server object
-
+
-Source code + +Expand source code +
def add(self, server_name, server):
     """Add new server to the rack
 
@@ -257,25 +265,29 @@ 

Args

def is_running(self, server_name)
-
+
-Source code + +Expand source code +
def is_running(self, server_name):
     return server_name in self._started
-def remove(self, server_name) +def remove(self, server_name: str)
-

Remove server (Stop it first if running)

+

Remove server (Stop it first if running)

Args

server_name : str
server name
-
+
-Source code + +Expand source code +
def remove(self, server_name: str):
     """Remove server (Stop it first if running)
 
@@ -292,17 +304,19 @@ 

Args

-def start(self, server_name=None, wait=False) +def start(self, server_name: str = None, wait: bool = False)
-

Start server by its name or start all servers

+

Start server by its name or start all servers

Args

server_name : str, optional
server name, if None will start all the servers. Defaults to None.
-
+
-Source code + +Expand source code +
def start(self, server_name: str = None, wait: bool = False):
     """Start server by its name or start all servers
 
@@ -324,17 +338,19 @@ 

Args

-def stop(self, server_name=None) +def stop(self, server_name: str = None)
-

Stop server by its name or stop all running servers

+

Stop server by its name or stop all running servers

Args

server_name : str, optional
server name, if None will stop all running servers. Defaults to None.
-
+
-Source code + +Expand source code +
def stop(self, server_name: str = None):
     """Stop server by its name or stop all running servers
 
@@ -393,9 +409,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/shell/config.html b/docs/api/jumpscale/shell/config.html index 5504b94ac..e64f061a1 100644 --- a/docs/api/jumpscale/shell/config.html +++ b/docs/api/jumpscale/shell/config.html @@ -3,15 +3,17 @@ - + jumpscale.shell.config API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.shell.config

-Source code + +Expand source code +
import better_exceptions
 import pudb
 import sys
@@ -300,11 +304,13 @@ 

Functions

def get_completions(self, document, complete_event)
-

get completions filtered and colored

+

get completions filtered and colored

To filter and color completions on type, we try get jedi completions first -and check their type, because prompt-toolkit.completion.Completion does not contain type information

+and check their type, because prompt-toolkit.completion.Completion does not contain type information

-Source code + +Expand source code +
def get_completions(self, document, complete_event):
     """
     get completions filtered and colored
@@ -341,9 +347,11 @@ 

Functions

def get_style_for_completion(completion)
-
+
-Source code + +Expand source code +
def get_style_for_completion(completion):
     base = "bg:%s fg:ansiblack"
     if completion.type == "function":
@@ -358,11 +366,13 @@ 

Functions

def patched_handle_exception(self, e)
-

a new handler for ptpython repl exceptions +

a new handler for ptpython repl exceptions it will call excepthook after ommitting all this framework's calls from traceback

-

for the original, see ptpython.repl.PythonInput._handle_exception

+

for the original, see ptpython.repl.PythonInput._handle_exception

-Source code + +Expand source code +
def patched_handle_exception(self, e):
     """
     a new handler for ptpython repl exceptions
@@ -398,9 +408,11 @@ 

Functions

def ptconfig(repl)
-
+
-Source code + +Expand source code +
def ptconfig(repl):
     repl.exit_message = "Bye!"
     repl.show_docstring = True
@@ -562,7 +574,7 @@ 

Functions

def sort_completions_key(completion)
-

sort completions according to their type

+

sort completions according to their type

Args

completion : jedi.api.classes.Completion
@@ -570,11 +582,13 @@

Args

Returns

-
int
+
int
sorting order
-
+
-Source code + +Expand source code +
def sort_completions_key(completion):
     """
     sort completions according to their type
@@ -622,9 +636,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/shell/index.html b/docs/api/jumpscale/shell/index.html index f120f5ba3..e40142a20 100644 --- a/docs/api/jumpscale/shell/index.html +++ b/docs/api/jumpscale/shell/index.html @@ -3,15 +3,17 @@ - + jumpscale.shell API documentation - - - - - + + + + + + +
@@ -23,7 +25,9 @@

Module jumpscale.shell

This module defines all of js-ng specific shell features

TODO outline.

-Source code + +Expand source code +
"""This module defines all of js-ng specific shell features
 
 # TODO outline.
@@ -37,7 +41,7 @@ 

Sub-modules

jumpscale.shell.config
-
+

@@ -70,9 +74,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/threesdk/container.html b/docs/api/jumpscale/threesdk/container.html index f85a2febd..510eccd9a 100644 --- a/docs/api/jumpscale/threesdk/container.html +++ b/docs/api/jumpscale/threesdk/container.html @@ -3,15 +3,17 @@ - + jumpscale.threesdk.container API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.threesdk.container

-Source code + +Expand source code +
from jumpscale.clients.docker.docker import DockerClient
 from jumpscale.core.dirs.dirs import Dirs
 from jumpscale.core.exceptions import Value
@@ -111,12 +115,13 @@ 

Classes

class Container -(*args, **kwargs)
-

Container management

+

Container management

-Source code + +Expand source code +
class Container:
     """Container management
     """
@@ -196,9 +201,11 @@ 

Static methods

def delete(name)
-
+
-Source code + +Expand source code +
@staticmethod
 def delete(name):
     Container.stop(name)
@@ -209,16 +216,18 @@ 

Static methods

def exec(name, cmd)
-

Execute command in container

+

Execute command in container

Args

name : str
name of the container
cmd : str or list
command to execute
-
+
-Source code + +Expand source code +
@staticmethod
 def exec(name, cmd):
     """Execute command in container
@@ -236,10 +245,10 @@ 

Args

-def install(name, image, development=False, volumes=None) +def install(name, image, development: bool = False, volumes=None)
-

Creates a container

+

Creates a container

Args

name : str
@@ -253,11 +262,13 @@

Args

Raises

-
Value
+
Value
Container with specified name already exists
-
+
-Source code + +Expand source code +
@staticmethod
 def install(name, image, development: bool = False, volumes=None):
     """Creates a container
@@ -286,14 +297,16 @@ 

Raises

def start(name)
-

Starts an existing container

+

Starts an existing container

Args

name : str
name of the container
-
+
-Source code + +Expand source code +
@staticmethod
 def start(name):
     """Starts an existing container
@@ -310,14 +323,16 @@ 

Args

def stop(name)
-

Stops an existing container

+

Stops an existing container

Args

name : str
name of the container
-
+
-Source code + +Expand source code +
@staticmethod
 def stop(name):
     """Stops an existing container
@@ -364,9 +379,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/threesdk/identitymanager.html b/docs/api/jumpscale/threesdk/identitymanager.html index 7abba8995..8f60cf0d0 100644 --- a/docs/api/jumpscale/threesdk/identitymanager.html +++ b/docs/api/jumpscale/threesdk/identitymanager.html @@ -3,15 +3,17 @@ - + jumpscale.threesdk.identitymanager API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.threesdk.identitymanager

-Source code + +Expand source code +
import binascii
 
 import requests
@@ -196,10 +200,10 @@ 

Classes

class IdentityInfo -(*args, **kwargs) +(identity, email, words, explorer)
-

IdentityInfo(identity, email, words, explorer)

+

IdentityInfo(identity, email, words, explorer)

Ancestors

  • builtins.tuple
  • @@ -208,30 +212,32 @@

    Instance variables

    var email
    -

    Alias for field number 1

    +

    Alias for field number 1

    var explorer
    -

    Alias for field number 3

    +

    Alias for field number 3

    var identity
    -

    Alias for field number 0

    +

    Alias for field number 0

    var words
    -

    Alias for field number 2

    +

    Alias for field number 2

class IdentityManager -(identity='', email=None, words=None, explorer=None) +(identity: str = '', email: str = None, words: str = None, explorer: str = None)
-
+
-Source code + +Expand source code +
class IdentityManager:
     def __init__(self, identity: str = "", email: str = None, words: str = None, explorer: str = None):
         self.identity = identity
@@ -380,9 +386,11 @@ 

Methods

def ask_identity(self, identity=None, explorer=None)
-
+
-Source code + +Expand source code +
def ask_identity(self, identity=None, explorer=None):
     def _fill_identity_args(identity, explorer):
         def fill_words():
@@ -465,9 +473,11 @@ 

Methods

def reset(self)
-
+
-Source code + +Expand source code +
def reset(self):
     self.identity = ""
     self.email = ""
@@ -515,9 +525,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/threesdk/index.html b/docs/api/jumpscale/threesdk/index.html index b50ba38bb..6cf3f611c 100644 --- a/docs/api/jumpscale/threesdk/index.html +++ b/docs/api/jumpscale/threesdk/index.html @@ -3,15 +3,17 @@ - + jumpscale.threesdk API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.threesdk

-Source code + +Expand source code +
from .threebot import ThreeBot as threebot
 import inspect
 import cgi
@@ -105,15 +109,15 @@ 

Sub-modules

jumpscale.threesdk.container
-
+
jumpscale.threesdk.identitymanager
-
+
jumpscale.threesdk.settings
-
+
@@ -126,9 +130,11 @@

Functions

def info()
-
+
-Source code + +Expand source code +
def info():
     print_formatted_text(HTML(_get_doc(__all__)))
@@ -140,12 +146,13 @@

Classes

class threebot -(*args, **kwargs)
-

Manage your threebot

+

Manage your threebot

-Source code + +Expand source code +
class ThreeBot(Container):
     """
     Manage your threebot
@@ -250,10 +257,10 @@ 

Ancestors

Static methods

-def install(name=None, image=None, identity=None, email=None, words=None, explorer=None, development=None) +def install(name=None, image=None, identity=None, email=None, words=None, explorer=None, development: bool = None)
-

Creates a threebot container

+

Creates a threebot container

Args

name : str, optional
@@ -273,13 +280,15 @@

Args

Raises

-
Value
+
Value
Container with specified name already exists
-
Value
+
Value
explorer not in mainnet, testnet, devnet
-
+
-Source code + +Expand source code +
@staticmethod
 def install(
     name=None, image=None, identity=None, email=None, words=None, explorer=None, development: bool = None,
@@ -323,17 +332,19 @@ 

Raises

-def jsng(name='3bot-ng') +def jsng(name='3bot-ng')
-

Get's shell in threebot

+

Get's shell in threebot

Args

name : str
name of the container (default: 3bot-ng)
-
+
-Source code + +Expand source code +
@staticmethod
 def jsng(name=DEFAULT_CONTAINER_NAME):
     """Get's shell in threebot
@@ -345,17 +356,19 @@ 

Args

-def restart(name='3bot-ng') +def restart(name='3bot-ng')
-

restart threebot installation with container

+

restart threebot installation with container

Args

name : str
name of the container (default: 3bot-ng)
-
+

-Source code + +Expand source code +
@staticmethod
 def restart(name=DEFAULT_CONTAINER_NAME):
     """restart threebot installation with container
@@ -368,17 +381,19 @@ 

Args

-def shell(name='3bot-ng') +def shell(name='3bot-ng')
-

Get's shell in threebot

+

Get's shell in threebot

Args

name : str
name of the container (default: 3bot-ng)
-
+
-Source code + +Expand source code +
@staticmethod
 def shell(name=DEFAULT_CONTAINER_NAME):
     """Get's shell in threebot
@@ -390,17 +405,19 @@ 

Args

-def start(name='3bot-ng') +def start(name='3bot-ng')
-

Start threebot container with threebot server

+

Start threebot container with threebot server

Args

name : str
name of the container (default: 3bot-ng)
-
+
-Source code + +Expand source code +
@staticmethod
 def start(name=DEFAULT_CONTAINER_NAME):
     """Start threebot container with threebot server
@@ -413,17 +430,19 @@ 

Args

-def stop(name='3bot-ng') +def stop(name='3bot-ng')
-

Stop threebot installation with container

+

Stop threebot installation with container

Args

name : str
name of the container (default: 3bot-ng)
-
+
-Source code + +Expand source code +
@staticmethod
 def stop(name=DEFAULT_CONTAINER_NAME):
     """Stop threebot installation with container
@@ -493,9 +512,7 @@ 

-

Generated by pdoc 0.6.4.

+

Generated by pdoc 0.10.0.

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/threesdk/settings.html b/docs/api/jumpscale/threesdk/settings.html index 75decc271..02559f717 100644 --- a/docs/api/jumpscale/threesdk/settings.html +++ b/docs/api/jumpscale/threesdk/settings.html @@ -3,15 +3,17 @@ - + jumpscale.threesdk.settings API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.threesdk.settings

-Source code + +Expand source code +
expert = False
@@ -49,9 +53,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/alerthandler/alerthandler.html b/docs/api/jumpscale/tools/alerthandler/alerthandler.html index 74bf7b7ef..bb937eb3d 100644 --- a/docs/api/jumpscale/tools/alerthandler/alerthandler.html +++ b/docs/api/jumpscale/tools/alerthandler/alerthandler.html @@ -3,15 +3,17 @@ - + jumpscale.tools.alerthandler.alerthandler API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.tools.alerthandler.alerthandler<
-Source code + +Expand source code +
from jumpscale.loader import j
 import gevent
 
@@ -305,9 +309,11 @@ 

Classes

class Alert
-
+
-Source code + +Expand source code +
class Alert:
     def __init__(self):
         self.id = None
@@ -349,9 +355,11 @@ 

Static methods

def loads(value)
-
+
-Source code + +Expand source code +
@classmethod
 def loads(cls, value):
     json = j.data.serializers.json.loads(value)
@@ -365,9 +373,11 @@ 

Instance variables

var identifier
-
+
-Source code + +Expand source code +
@property
 def identifier(self):
     return _get_identifier(self.app_name, self.message, self.public_message, self.category, self.type)
@@ -375,9 +385,11 @@

Instance variables

var json
-
+
-Source code + +Expand source code +
@property
 def json(self):
     return self.__dict__
@@ -390,9 +402,11 @@

Methods

def dumps(self)
-
+
-Source code + +Expand source code +
def dumps(self):
     return j.data.serializers.json.dumps(self.__dict__)
@@ -403,9 +417,11 @@

Methods

class AlertsHandler
-
+
-Source code + +Expand source code +
class AlertsHandler:
     def __init__(self):
         self._rkey = "alerts"
@@ -635,9 +651,11 @@ 

Instance variables

var db
-
+
-Source code + +Expand source code +
@property
 def db(self):
     if self._db is None:
@@ -649,25 +667,24 @@ 

Instance variables

Methods

-def alert_raise(self, app_name, message, public_message='', category='', alert_type='event_system', level=40, data=None, timestamp=None, traceback=None) +def alert_raise(self, app_name, message, public_message: str = '', category: str = '', alert_type: str = 'event_system', level: int = 40, data: dict = None, timestamp: float = None, traceback: dict = None) ‑> Alert
-

Raise a new alert

+

Raise a new alert

Arguments

-

message {str} – alert message -Keyword Arguments: +

message {str} – alert message

+

Keyword Arguments: public_message {str} – alert public message (default: {""}) category {str} – alert category (default: {""}) alert_type {str} – alert type (default: {"event_system"}) level {int} – alert level (default: {40}) traceback {dict} – alert traceback (default: {None})

Returns

-
-
Alertalert object
-
 
-
+

Alert – alert object

-Source code + +Expand source code +
def alert_raise(
     self,
     app_name,
@@ -733,17 +750,16 @@ 

Returns

-def count(self) +def count(self) ‑> int
-

Gets alerts count

+

Gets alerts count

Returns

-
-
inttotal number of alerts
-
 
-
+

int – total number of alerts

-Source code + +Expand source code +
def count(self) -> int:
     """Gets alerts count
 
@@ -754,20 +770,22 @@ 

Returns

-def delete(self, alert_id=None, identifier=None) +def delete(self, alert_id: int = None, identifier: str = None)
-

Delete alert by its id or identifier

+

Delete alert by its id or identifier

Raises

-
j.core.exceptions.Value: invalid arguments
-
 
-
Keyword Arguments:
-
alert_id {int} – alert id (default: {None}) -identifier {str} – alert identifier (default: {None})
-
+
j.core.exceptions.Value
+
invalid arguments
+
+

Keyword Arguments: +alert_id {int} – alert id (default: {None}) +identifier {str} – alert identifier (default: {None})

-Source code + +Expand source code +
def delete(self, alert_id: int = None, identifier: str = None):
     """Delete alert by its id or identifier
 
@@ -790,19 +808,21 @@ 

Raises

def delete_all(self)
-

Deletes all alerts

+

Deletes all alerts

-Source code + +Expand source code +
def delete_all(self):
     """Deletes all alerts"""
     self.db.delete(self._rkey, self._rkey_id)
-def find(self, app_name='', category='', message='', pid=None, start_time=None, end_time=None) +def find(self, app_name: str = '', category: str = '', message: str = '', pid: int = None, start_time: int = None, end_time: int = None) ‑> list
-

Find alerts

+

Find alerts

Keyword Arguments: app_name (str): filter by allert app name (default: {""}) @@ -812,12 +832,11 @@

Raises

start_time {int} – filter by start time (default: {None}) end_time {int} – filter by end time (default: {None})

Returns

-
-
list of Alert objects
-
 
-
+

list of Alert objects

-Source code + +Expand source code +
def find(
     self,
     app_name: str = "",
@@ -881,28 +900,27 @@ 

Returns

-def get(self, alert_id=None, identifier=None, die=True) +def get(self, alert_id: int = None, identifier: str = None, die: bool = True) ‑> Alert
-

Get alert by its id or identifier

+

Get alert by its id or identifier

Keyword Arguments: alert_id {int} – alert id (default: {None}) identifier {str} – alert identifier (default: {None}) die {bool} – flag to rasie exception if alert is not found (default: {True})

Raises

-
j.core.exceptions.NotFound: alert is not found
-
 
-
j.core.exceptions.Value: invalid arguments
-
 
+
j.core.exceptions.NotFound
+
alert is not found
+
j.core.exceptions.Value
+
invalid arguments

Returns

-
-
Alert – [description]
-
 
-
+

Alert – [description]

-Source code + +Expand source code +
def get(self, alert_id: int = None, identifier: str = None, die: bool = True) -> Alert:
     """Get alert by its id or identifier
 
@@ -931,19 +949,18 @@ 

Returns

-def register_handler(self, handler, level=40) +def register_handler(self, handler: , level: int = 40)
-

Register new alert handler

+

Register new alert handler

Arguments

-
-
handler : callable
-
error handler callable
-
+

handler (callable): error handler callable

Keyword Arguments: -level (int): exception level (default: {40})

+level (int): exception level (default: {40})

-Source code + +Expand source code +
def register_handler(self, handler: callable, level: int = 40):
     """Register new alert handler
 
@@ -961,9 +978,11 @@ 

Arguments

def reset(self)
-

Delete all alerts and reset the db

+

Delete all alerts and reset the db

-Source code + +Expand source code +
def reset(self):
     """Delete all alerts and reset the db"""
     self.delete_all()
@@ -1017,9 +1036,7 @@ 

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/alerthandler/index.html b/docs/api/jumpscale/tools/alerthandler/index.html index cb5266efb..46e79c620 100644 --- a/docs/api/jumpscale/tools/alerthandler/index.html +++ b/docs/api/jumpscale/tools/alerthandler/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.alerthandler API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.tools.alerthandler

-Source code + +Expand source code +
def export_module_as():
     from .alerthandler import AlertsHandler
     return AlertsHandler()
@@ -32,7 +36,7 @@

Sub-modules

jumpscale.tools.alerthandler.alerthandler
-
+
@@ -45,9 +49,11 @@

Functions

def export_module_as()
-
+
-Source code + +Expand source code +
def export_module_as():
     from .alerthandler import AlertsHandler
     return AlertsHandler()
@@ -83,9 +89,7 @@

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/codeloader/index.html b/docs/api/jumpscale/tools/codeloader/index.html index dc841d903..89f6ca247 100644 --- a/docs/api/jumpscale/tools/codeloader/index.html +++ b/docs/api/jumpscale/tools/codeloader/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.codeloader API documentation - - - - - + + + + + + +
@@ -28,12 +30,14 @@

Module jumpscale.tools.codeloader

z = a+b

Using the codeloader tool

-
JS-NG> m = j.tools.codeloader.load_python_module("/tmp/hello.py")
+
JS-NG> m = j.tools.codeloader.load_python_module("/tmp/hello.py")
 JS-NG> m.a
 5
 
-Source code + +Expand source code +
"""Loads a module from path
 
 Let's make sure we have a python module in path `/tmp/hello.py`
@@ -94,10 +98,10 @@ 

Using the codeloader tool

Functions

-def load_python_module(module_path, force_reload=False) +def load_python_module(module_path: str, force_reload: bool = False) ‑> module
-

Loads python module by path

+

Loads python module by path

Args

module_path : str
@@ -107,11 +111,13 @@

Args

Returns

-
types.ModuleType: module object
-
 
-
+
types.ModuleType
+
module object
+
-Source code + +Expand source code +
def load_python_module(module_path: str, force_reload: bool = False) -> types.ModuleType:
     """
     Loads python module by path
@@ -164,9 +170,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/console/index.html b/docs/api/jumpscale/tools/console/index.html index 605f7d437..1d72f308e 100644 --- a/docs/api/jumpscale/tools/console/index.html +++ b/docs/api/jumpscale/tools/console/index.html @@ -3,17 +3,19 @@ - + jumpscale.tools.console API documentation - - - - - + + + + + + +
@@ -23,15 +25,17 @@

Module jumpscale.tools.console

Console module helps with coloring in the console and asking for input from the user.

-
JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")                                                                     
-JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")                                                              
+
JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")
+JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")
 
-Source code + +Expand source code +
"""Console module helps with coloring in the console and asking for input from the user.
 ```
-JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")                                                                     
-JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")                                                              
+JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")
+JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")
 ```
 
 """
@@ -73,11 +77,11 @@ 

Module jumpscale.tools.console

def ask_password(prompt="Password : ", forbiddens=[]): """Prompt the user for a password without echoing - + Keyword Arguments: prompt {str} -- the question message (default: {"Password : "}) forbiddens {list} -- the list of bad passwords (default: {[]}) - + Returns: str -- the appropriate input password """ @@ -90,12 +94,12 @@

Module jumpscale.tools.console

def ask_yes_no(prompt="[y/n] :", default="y", valid=["y", "n"]): """Display a yes/no question and loop until a valid answer is entered - + Keyword Arguments: prompt {str} -- the question message (default: {'[y/n] :'}) default {str} -- the default answer if there is no answer (default: {"y"}) valid {list} -- the list of appropriate answers (default: {["y", "n"]}) - + Returns: str -- the answer """ @@ -118,14 +122,14 @@

Module jumpscale.tools.console

def ask_int_in_range(mini, maxi, prompt="Type int :"): """Get an integer response between two integer on asked question - + Arguments: mini {int} -- the minimum value for the number maxi {int} -- the maximum value for the number - + Keyword Arguments: prompt {str} -- the question message (default: {"Type int :"}) - + Returns: int -- the input number on the range provided """ @@ -148,14 +152,14 @@

Module jumpscale.tools.console

def ask_float_in_range(mini, maxi, prompt="Type float :"): """Get an float response between two float on asked question - + Arguments: mini {float} -- the minimum value for the number maxi {float} -- the maximum value for the number - + Keyword Arguments: prompt {str} -- the question message (default: {"Type float :"}) - + Returns: float -- the input number on the range provided """ @@ -180,11 +184,11 @@

Module jumpscale.tools.console

def ask_choice(prompt="Type choice number : ", choices_list=[]): """Get an option from provided list - + Keyword Arguments: prompt {str} -- the question message (default: {"Type choice number : "}) choices_list {list} -- the available options (default: {[]}) - + Returns: str -- the chosen option """ @@ -198,13 +202,13 @@

Module jumpscale.tools.console

def ask_multi_choices(prompt="Add to choices : ", choices_list=[], to_save="s", to_quit="q"): """Collect multi choices from list - + Keyword Arguments: prompt {str} -- the question method (default: {"Add to choices : "}) choices_list {list} -- the available options (default: {[]}) to_save {str} -- escape and save choices (default: {"s"}) to_quit {str} -- escape without saving (default: {"q"}) - + Returns: list -- list of the selected choices """ @@ -227,11 +231,11 @@

Module jumpscale.tools.console

def ask_multi_lines(prompt="Type :", escape_string="."): """Get input from user provided multilines - + Keyword Arguments: prompt {str} -- the question message (default: {"Type :"}) escape_string {str} -- escape character (default: {"."}) - + Returns: str -- the text seperated by lines """ @@ -245,10 +249,10 @@

Module jumpscale.tools.console

def ask_string(prompt="Type :"): """Just input function - + Keyword Arguments: prompt {str} -- the question message (default: {"Type :"}) - + Returns: str -- the string input """ @@ -271,27 +275,26 @@

Module jumpscale.tools.console

Functions

-def ask_choice(prompt='Type choice number : ', choices_list=[]) +def ask_choice(prompt='Type choice number : ', choices_list=[])
-

Get an option from provided list

+

Get an option from provided list

Keyword Arguments: prompt {str} – the question message (default: {"Type choice number : "}) choices_list {list} – the available options (default: {[]})

Returns

-
-
strthe chosen option
-
 
-
+

str – the chosen option

-Source code + +Expand source code +
def ask_choice(prompt="Type choice number : ", choices_list=[]):
     """Get an option from provided list
-    
+
     Keyword Arguments:
         prompt {str} -- the question message (default: {"Type choice number : "})
         choices_list {list} -- the available options (default: {[]})
-    
+
     Returns:
         str -- the chosen option
     """
@@ -304,12 +307,14 @@ 

Returns

-def ask_float(prompt='Type float :') +def ask_float(prompt='Type float :')
-
+
-Source code + +Expand source code +
def ask_float(prompt="Type float :"):
     try:
         return float(input(prompt))
@@ -318,32 +323,31 @@ 

Returns

-def ask_float_in_range(mini, maxi, prompt='Type float :') +def ask_float_in_range(mini, maxi, prompt='Type float :')
-

Get an float response between two float on asked question

+

Get an float response between two float on asked question

Arguments

mini {float} – the minimum value for the number -maxi {float} – the maximum value for the number -Keyword Arguments: +maxi {float} – the maximum value for the number

+

Keyword Arguments: prompt {str} – the question message (default: {"Type float :"})

Returns

-
-
floatthe input number on the range provided
-
 
-
+

float – the input number on the range provided

-Source code + +Expand source code +
def ask_float_in_range(mini, maxi, prompt="Type float :"):
     """Get an float response between two float on asked question
-    
+
     Arguments:
         mini {float} -- the minimum value for the number
         maxi {float} -- the maximum value for the number
-    
+
     Keyword Arguments:
         prompt {str} -- the question message (default: {"Type float :"})
-    
+
     Returns:
         float -- the input number on the range provided
     """
@@ -358,12 +362,14 @@ 

Returns

-def ask_int(prompt='Type int :') +def ask_int(prompt='Type int :')
-
+
-Source code + +Expand source code +
def ask_int(prompt="Type int :"):
     try:
         return int(input(prompt))
@@ -372,32 +378,31 @@ 

Returns

-def ask_int_in_range(mini, maxi, prompt='Type int :') +def ask_int_in_range(mini, maxi, prompt='Type int :')
-

Get an integer response between two integer on asked question

+

Get an integer response between two integer on asked question

Arguments

mini {int} – the minimum value for the number -maxi {int} – the maximum value for the number -Keyword Arguments: +maxi {int} – the maximum value for the number

+

Keyword Arguments: prompt {str} – the question message (default: {"Type int :"})

Returns

-
-
intthe input number on the range provided
-
 
-
+

int – the input number on the range provided

-Source code + +Expand source code +
def ask_int_in_range(mini, maxi, prompt="Type int :"):
     """Get an integer response between two integer on asked question
-    
+
     Arguments:
         mini {int} -- the minimum value for the number
         maxi {int} -- the maximum value for the number
-    
+
     Keyword Arguments:
         prompt {str} -- the question message (default: {"Type int :"})
-    
+
     Returns:
         int -- the input number on the range provided
     """
@@ -412,31 +417,30 @@ 

Returns

-def ask_multi_choices(prompt='Add to choices : ', choices_list=[], to_save='s', to_quit='q') +def ask_multi_choices(prompt='Add to choices : ', choices_list=[], to_save='s', to_quit='q')
-

Collect multi choices from list

+

Collect multi choices from list

Keyword Arguments: prompt {str} – the question method (default: {"Add to choices : "}) choices_list {list} – the available options (default: {[]}) to_save {str} – escape and save choices (default: {"s"}) to_quit {str} – escape without saving (default: {"q"})

Returns

-
-
listlist of the selected choices
-
 
-
+

list – list of the selected choices

-Source code + +Expand source code +
def ask_multi_choices(prompt="Add to choices : ", choices_list=[], to_save="s", to_quit="q"):
     """Collect multi choices from list
-    
+
     Keyword Arguments:
         prompt {str} -- the question method (default: {"Add to choices : "})
         choices_list {list} -- the available options (default: {[]})
         to_save {str} -- escape and save choices (default: {"s"})
         to_quit {str} -- escape without saving (default: {"q"})
-    
+
     Returns:
         list -- list of the selected choices
     """
@@ -458,27 +462,26 @@ 

Returns

-def ask_multi_lines(prompt='Type :', escape_string='.') +def ask_multi_lines(prompt='Type :', escape_string='.')
-

Get input from user provided multilines

+

Get input from user provided multilines

Keyword Arguments: prompt {str} – the question message (default: {"Type :"}) escape_string {str} – escape character (default: {"."})

Returns

-
-
strthe text seperated by lines
-
 
-
+

str – the text seperated by lines

-Source code + +Expand source code +
def ask_multi_lines(prompt="Type :", escape_string="."):
     """Get input from user provided multilines
-    
+
     Keyword Arguments:
         prompt {str} -- the question message (default: {"Type :"})
         escape_string {str} -- escape character (default: {"."})
-    
+
     Returns:
         str -- the text seperated by lines
     """
@@ -491,27 +494,26 @@ 

Returns

-def ask_password(prompt='Password : ', forbiddens=[]) +def ask_password(prompt='Password : ', forbiddens=[])
-

Prompt the user for a password without echoing

+

Prompt the user for a password without echoing

Keyword Arguments: prompt {str} – the question message (default: {"Password : "}) forbiddens {list} – the list of bad passwords (default: {[]})

Returns

-
-
strthe appropriate input password
-
 
-
+

str – the appropriate input password

-Source code + +Expand source code +
def ask_password(prompt="Password : ", forbiddens=[]):
     """Prompt the user for a password without echoing
-    
+
     Keyword Arguments:
         prompt {str} -- the question message (default: {"Password : "})
         forbiddens {list} -- the list of bad passwords (default: {[]})
-    
+
     Returns:
         str -- the appropriate input password
     """
@@ -523,25 +525,24 @@ 

Returns

-def ask_string(prompt='Type :') +def ask_string(prompt='Type :')
-

Just input function

+

Just input function

Keyword Arguments: prompt {str} – the question message (default: {"Type :"})

Returns

-
-
strthe string input
-
 
-
+

str – the string input

-Source code + +Expand source code +
def ask_string(prompt="Type :"):
     """Just input function
-    
+
     Keyword Arguments:
         prompt {str} -- the question message (default: {"Type :"})
-    
+
     Returns:
         str -- the string input
     """
@@ -551,29 +552,28 @@ 

Returns

-def ask_yes_no(prompt='[y/n] :', default='y', valid=['y', 'n']) +def ask_yes_no(prompt='[y/n] :', default='y', valid=['y', 'n'])
-

Display a yes/no question and loop until a valid answer is entered

+

Display a yes/no question and loop until a valid answer is entered

Keyword Arguments: prompt {str} – the question message (default: {'[y/n] :'}) default {str} – the default answer if there is no answer (default: {"y"}) valid {list} – the list of appropriate answers (default: {["y", "n"]})

Returns

-
-
strthe answer
-
 
-
+

str – the answer

-Source code + +Expand source code +
def ask_yes_no(prompt="[y/n] :", default="y", valid=["y", "n"]):
     """Display a yes/no question and loop until a valid answer is entered
-    
+
     Keyword Arguments:
         prompt {str} -- the question message (default: {'[y/n] :'})
         default {str} -- the default answer if there is no answer (default: {"y"})
         valid {list} -- the list of appropriate answers (default: {["y", "n"]})
-    
+
     Returns:
         str -- the answer
     """
@@ -591,9 +591,11 @@ 

Returns

def format(s)
-
+
-Source code + +Expand source code +
def format(s):
     return s.format(**NAMES_TO_COLORS)
@@ -602,15 +604,17 @@

Returns

def printcolors(s)
-
>>> j.tools.console.printcolors("{RED}Hello world")
+
>>> j.tools.console.printcolors("{RED}Hello world")
 Hello world
->>> j.tools.console.printcolors("{GREEN}Hello world")
+>>> j.tools.console.printcolors("{GREEN}Hello world")
 Hello world
 

Arguments: -s {[type]} – [description]

+s {[type]} – [description]

-Source code + +Expand source code +
def printcolors(s):
     """
         >>> j.tools.console.printcolors("{RED}Hello world")
@@ -628,9 +632,11 @@ 

Returns

def printobj(obj)
-
+
-Source code + +Expand source code +
def printobj(obj):
     from pprint import pprint
 
@@ -674,9 +680,7 @@ 

Index

- - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/depsresolver/depsresolver.html b/docs/api/jumpscale/tools/depsresolver/depsresolver.html index 677cc1ec8..84492d7e6 100644 --- a/docs/api/jumpscale/tools/depsresolver/depsresolver.html +++ b/docs/api/jumpscale/tools/depsresolver/depsresolver.html @@ -3,15 +3,17 @@ - + jumpscale.tools.depsresolver.depsresolver API documentation - - - - - + + + + + + +
@@ -21,7 +23,9 @@

Module jumpscale.tools.depsresolver.depsresolver<
-Source code + +Expand source code +
from dataclasses import dataclass, field
 from typing import List, Dict, Tuple, Callable
 from enum import Enum
@@ -150,12 +154,14 @@ 

Module jumpscale.tools.depsresolver.depsresolver<

Functions

-def graph_has_cycle(graph) +def graph_has_cycle(graph: Dict[str, List[str]]) ‑> Tuple[bool, Dict[str, str]]
-
+
-Source code + +Expand source code +
def graph_has_cycle(graph: Dict[str, List[str]]) -> Tuple[bool, Dict[str, str]]:
     colors: Dict[str, NodeColor] = {}
     for node in graph:
@@ -173,12 +179,14 @@ 

Functions

-def has_cycle_dfs(graph, node, colors, has_cycle, parent_map) +def has_cycle_dfs(graph: Dict[str, List[str]], node: str, colors: Dict[str, NodeColor], has_cycle: List[bool], parent_map: Dict[str, str])
-
+
-Source code + +Expand source code +
def has_cycle_dfs(
     graph: Dict[str, List[str]],
     node: str,
@@ -208,13 +216,16 @@ 

Classes

class DepsResolver -(tasksgraph=, tasks=) +(tasksgraph: Dict[str, List[str]] = <factory>, tasks: Dict[str, Task] = <factory>)
-

DepsResolver(tasksgraph: Dict[str, List[str]] = , tasks: Dict[str, jumpscale.tools.depsresolver.depsresolver.Task] = )

+

DepsResolver(tasksgraph: Dict[str, List[str]] = , tasks: Dict[str, jumpscale.tools.depsresolver.depsresolver.Task] = )

-Source code -
class DepsResolver:
+
+Expand source code
+
+
@dataclass
+class DepsResolver:
     tasksgraph: Dict[str, List[str]] = field(default_factory=dict)
     tasks: Dict[str, Task] = field(default_factory=dict)
 
@@ -249,15 +260,28 @@ 

Classes

self._run_task_helper(subtask, deps, seen) deps.append(task_name)
+

Class variables

+
+
var tasks : Dict[str, Task]
+
+
+
+
var tasksgraph : Dict[str, List[str]]
+
+
+
+

Methods

-def add_task(self, task_name, deps, action) +def add_task(self, task_name: str, deps: List[str], action: str)
-
+
-Source code + +Expand source code +
def add_task(self, task_name: str, deps: List[str], action: str):
     thetask = Task(task_name, deps, action)
     self.tasksgraph[task_name] = deps
@@ -265,12 +289,14 @@ 

Methods

-def run_task(self, task_name) +def run_task(self, task_name: str)
-
+
-Source code + +Expand source code +
def run_task(self, task_name: str):
     has_cycle, parent_map = graph_has_cycle(self.tasksgraph)
     if has_cycle:
@@ -292,12 +318,14 @@ 

Methods

class NodeColor -(*args, **kwargs) +(value, names=None, *, module=None, qualname=None, type=None, start=1)
-

An enumeration.

+

An enumeration.

-Source code + +Expand source code +
class NodeColor(Enum):
     White = 0
     Gray = 1
@@ -311,31 +339,49 @@ 

Class variables

var Black
-
+
var Gray
-
+
var White
-
+
class Task -(name, requires, action) +(name: str, requires: List[str], action: Callable)
-

Task(name: str, requires: List[str], action: Callable)

+

Task(name: str, requires: List[str], action: Callable)

-Source code -
class Task:
+
+Expand source code
+
+
@dataclass
+class Task:
     name: str
     requires: List[str]
     action: Callable
+

Class variables

+
+
var action : Callable
+
+
+
+
var name : str
+
+
+
+
var requires : List[str]
+
+
+
+
@@ -364,6 +410,8 @@

  • add_task
  • run_task
  • +
  • tasks
  • +
  • tasksgraph
  • @@ -376,6 +424,11 @@

    Task

    +
  • @@ -383,9 +436,7 @@

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/depsresolver/index.html b/docs/api/jumpscale/tools/depsresolver/index.html index a0a05f71e..29d10d4f9 100644 --- a/docs/api/jumpscale/tools/depsresolver/index.html +++ b/docs/api/jumpscale/tools/depsresolver/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.depsresolver API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.tools.depsresolver

    -Source code + +Expand source code +
    def export_module_as():
         from .depsresolver import DepsResolver
     
    @@ -33,7 +37,7 @@ 

    Sub-modules

    jumpscale.tools.depsresolver.depsresolver
    -
    +
    @@ -46,9 +50,11 @@

    Functions

    def export_module_as()
    -
    +
    -Source code + +Expand source code +
    def export_module_as():
         from .depsresolver import DepsResolver
     
    @@ -85,9 +91,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/errorhandler/errorhandler.html b/docs/api/jumpscale/tools/errorhandler/errorhandler.html index 9673c0b38..0fcc1d12d 100644 --- a/docs/api/jumpscale/tools/errorhandler/errorhandler.html +++ b/docs/api/jumpscale/tools/errorhandler/errorhandler.html @@ -3,15 +3,17 @@ - + jumpscale.tools.errorhandler.errorhandler API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.tools.errorhandler.errorhandler<
    -Source code + +Expand source code +
    import inspect
     import sys
     import traceback
    @@ -176,9 +180,11 @@ 

    Classes

    class ErrorHandler
    -
    +
    -Source code + +Expand source code +
    class ErrorHandler:
         def __init__(self):
             self.handlers = []
    @@ -318,9 +324,11 @@ 

    Methods

    def excepthook(self, ttype, tvalue, tb)
    -

    exception hook handler

    +

    exception hook handler

    -Source code + +Expand source code +
    def excepthook(self, ttype, tvalue, tb):
         """exception hook handler"""
         self._handle_exception(ttype, tvalue, tb)
    @@ -330,8 +338,8 @@

    Methods

    def get_traceback(self, exc_info=None)
    -

    Get a trackback information as a dict, suitable to used with error/alert handlers

    -
        if `exe_info` is not passed, `sys.exc_info()` will be used instead.
    +

    Get a trackback information as a dict, suitable to used with error/alert handlers

    +
        if <code>exe\_info</code> is not passed, <code>sys.exc\_info()</code> will be used instead.
     
         Example traceback information:
     
    @@ -358,9 +366,11 @@ 

    Methods

    Returns: dict: raw and stacktrace information as a dict, alongside process id -
    +
    -Source code + +Expand source code +
    def get_traceback(self, exc_info=None):
         """Get a trackback information as a dict, suitable to used with error/alert handlers
     
    @@ -402,19 +412,21 @@ 

    Methods

    -def handle_exception(self, exception, level=40, die=False, log=True, category='', data=None) +def handle_exception(self, exception: Exception, level: int = 40, die: bool = False, log: bool = True, category: str = '', data: dict = None)
    -

    Hndler exception

    +

    Hndler exception

    Arguments

    -

    exception {Exception} – the exception object to handle -Keyword Arguments: +

    exception {Exception} – the exception object to handle

    +

    Keyword Arguments: level {int} – exception level (default: {40}) die {bool} – optional flag to exit after handling the exception (default: {True}) log {bool} – option flag to log the exception (default: {True}) -category {str} – category (default: {""})

    +category {str} – category (default: {""})

    -Source code + +Expand source code +
    def handle_exception(
         self,
         exception: Exception,
    @@ -440,16 +452,18 @@ 

    Arguments

    -def register_handler(self, handler, level=40) +def register_handler(self, handler: , level: int = 40)
    -

    Register new error handler

    +

    Register new error handler

    Arguments

    -

    handler {callable} – error handler callable -Keyword Arguments: -level {int} – exception level (default: {40})

    +

    handler {callable} – error handler callable

    +

    Keyword Arguments: +level {int} – exception level (default: {40})

    -Source code + +Expand source code +
    def register_handler(self, handler: callable, level: int = 40):
         """Register new error handler
     
    @@ -496,9 +510,7 @@ 

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/errorhandler/index.html b/docs/api/jumpscale/tools/errorhandler/index.html index 142bbeb81..26b974391 100644 --- a/docs/api/jumpscale/tools/errorhandler/index.html +++ b/docs/api/jumpscale/tools/errorhandler/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.errorhandler API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.tools.errorhandler

    -Source code + +Expand source code +
    def export_module_as():
         from .errorhandler import ErrorHandler
     
    @@ -33,7 +37,7 @@ 

    Sub-modules

    jumpscale.tools.errorhandler.errorhandler
    -
    +
    @@ -46,9 +50,11 @@

    Functions

    def export_module_as()
    -
    +
    -Source code + +Expand source code +
    def export_module_as():
         from .errorhandler import ErrorHandler
     
    @@ -85,9 +91,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/git/index.html b/docs/api/jumpscale/tools/git/index.html index 7f211aad0..2cafb61ac 100644 --- a/docs/api/jumpscale/tools/git/index.html +++ b/docs/api/jumpscale/tools/git/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.git API documentation - - - - - + + + + + + +
    @@ -23,7 +25,9 @@

    Module jumpscale.tools.git

    Git module helps with everything around git management like pulling, cloning .. etc

    TODO: examples

    -Source code + +Expand source code +
    """Git module helps with everything around git management like pulling, cloning .. etc
     
     # TODO: examples
    @@ -231,7 +235,7 @@ 

    TODO: examples

    def get_latest_remote_tag(repo_path): """ - Get the latest tag of a remote repository + retrive the latest tag of a remote upstream repository Args: repo_path (str): path to the local git repository @@ -239,13 +243,12 @@

    TODO: examples

    Returns: str: the latest tag of the remote repository """ - try: - _, out, _ = j.sals.process.execute( - "git ls-remote --tags --refs --sort='v:refname' | tail -n1 | sed 's/.*\///'", cwd=repo_path - ) - latest_remote_tag = out.rstrip("\n") - except Exception as e: - raise j.exceptions.Runtime(f"Failed to fetch remote releases. {str(e)}") + rc, out, err = j.sals.process.execute( + "git ls-remote --tags --refs | sort -t '/' -k 3 -V | tail -n1 | sed 's/.*\///'", cwd=repo_path + ) + if rc != 0: + raise j.exceptions.Runtime(f"Failed to fetch latest remote release. {err}") + latest_remote_tag = out.rstrip("\n") return latest_remote_tag

    @@ -257,10 +260,10 @@

    TODO: examples

    Functions

    -def clone_repo(url, dest, branch_or_tag='', depth=0, commit_id='') +def clone_repo(url: str, dest: str, branch_or_tag='', depth=0, commit_id='')
    -

    Clones repo with sepcified url at specified dest

    +

    Clones repo with sepcified url at specified dest

    Args

    url : str
    @@ -273,9 +276,11 @@

    Args

    specify the depth of the clone. Defaults to 0.
    commit_id : int, optional
    commit to checkout to. Defauls to "".
    -
    +
    -Source code + +Expand source code +
    def clone_repo(url: str, dest: str, branch_or_tag="", depth=0, commit_id=""):
         """Clones repo with sepcified url at specified dest
     
    @@ -307,10 +312,10 @@ 

    Args

    -def ensure_repo(url, dest='', branch_or_tag='', commit_id='', discard_changes=False, depth=0) +def ensure_repo(url: str, dest='', branch_or_tag='', commit_id='', discard_changes=False, depth=0)
    -

    Makes sure that repo exists in specified dest with correct branch

    +

    Makes sure that repo exists in specified dest with correct branch

    Args

    url : str
    @@ -325,9 +330,11 @@

    Args

    Will remove changes in repo if True during pull. Defaults to False.
    depth : int, optional
    specify the depth of the clone. Defaults to 0.
    -
    +
    -Source code + +Expand source code +
    def ensure_repo(url: str, dest="", branch_or_tag="", commit_id="", discard_changes=False, depth=0):
         """Makes sure that repo exists in specified dest with correct branch
     
    @@ -350,10 +357,10 @@ 

    Args

    -def find(account='', name='') +def find(account='', name='')
    -

    find all repo paths in j.core.dirs.CODEDIR

    +

    find all repo paths in j.core.dirs.CODEDIR

    Args

    account : str, optional
    @@ -363,11 +370,13 @@

    Args

    Returns

    -
    list
    +
    list
    list of repos path
    -
    +
    -Source code + +Expand source code +
    def find(account="", name=""):
         """find all repo paths in j.core.dirs.CODEDIR
     
    @@ -396,12 +405,14 @@ 

    Returns

    def find_git_path(path, die=True)
    -

    given a path, check if this path or any of its parents is a git repo, return the first git repo +

    given a path, check if this path or any of its parents is a git repo, return the first git repo :param path: (String) path from where to start search :returns (String) the first path which is a git repo -:raises Exception when no git path can be found

    +:raises Exception when no git path can be found

    -Source code + +Expand source code +
    def find_git_path(path, die=True):
         """
         given a path, check if this path or any of its parents is a git repo, return the first git repo
    @@ -421,7 +432,7 @@ 

    Returns

    def get_latest_remote_tag(repo_path)
    -

    Get the latest tag of a remote repository

    +

    retrive the latest tag of a remote upstream repository

    Args

    repo_path : str
    @@ -429,14 +440,16 @@

    Args

    Returns

    -
    str
    +
    str
    the latest tag of the remote repository
    -
    +
    -Source code + +Expand source code +
    def get_latest_remote_tag(repo_path):
         """
    -    Get the latest tag of a remote repository
    +    retrive the latest tag of a remote upstream repository
     
         Args:
             repo_path (str): path to the local git repository
    @@ -444,28 +457,29 @@ 

    Returns

    Returns: str: the latest tag of the remote repository """ - try: - _, out, _ = j.sals.process.execute( - "git ls-remote --tags --refs --sort='v:refname' | tail -n1 | sed 's/.*\///'", cwd=repo_path - ) - latest_remote_tag = out.rstrip("\n") - except Exception as e: - raise j.exceptions.Runtime(f"Failed to fetch remote releases. {str(e)}") + rc, out, err = j.sals.process.execute( + "git ls-remote --tags --refs | sort -t '/' -k 3 -V | tail -n1 | sed 's/.*\///'", cwd=repo_path + ) + if rc != 0: + raise j.exceptions.Runtime(f"Failed to fetch latest remote release. {err}") + latest_remote_tag = out.rstrip("\n") return latest_remote_tag
    -def get_path_from_url(url) +def get_path_from_url(url: str)
    -

    returns repo path in the filesystem based on the urk

    +

    returns repo path in the filesystem based on the urk

    Args

    url : str
    repo url
    -
    +
    -Source code + +Expand source code +
    def get_path_from_url(url: str):
         """returns repo path in the filesystem based on the urk
     
    @@ -491,7 +505,7 @@ 

    Args

    def giturl_parse(url)
    -

    :return +

    +- https://github.com/threefoldtech/jumpscale_/jumpscaleX_core/tree/master/lib/Jumpscale/tools/docsite/macros

    -Source code + +Expand source code +
    def giturl_parse(url):
         """
         :return
    @@ -538,10 +554,10 @@ 

    Args

    -def pull_repo(path, discard_changes=False, revision='') +def pull_repo(path: str, discard_changes=False, revision='')
    -

    Pull repo at the given path

    +

    Pull repo at the given path

    Args

    path : str
    @@ -550,9 +566,11 @@

    Args

    Will remove changes in repo if True. Defaults to False.
    revision : str, optional
    change to specified branch, tag, commit. Defaults to "".
    -
    +
    -Source code + +Expand source code +
    def pull_repo(path: str, discard_changes=False, revision=""):
         """Pull repo at the given path
     
    @@ -573,10 +591,12 @@ 

    Args

    def rewrite_git_https_url_to_ssh(url)
    -

    :param url: (str) https url to rewrite as ssh -:return (tuple) repo name and rewritten ssh url

    +

    :param url: (str) https url to rewrite as ssh +:return (tuple) repo name and rewritten ssh url

    -Source code + +Expand source code +
    def rewrite_git_https_url_to_ssh(url):
         """
         :param url: (str) https url to rewrite as ssh
    @@ -592,12 +612,14 @@ 

    Args

    def rewrite_git_ssh_url_to_https(url, login=None, passwd=None)
    -

    :param url: (str) ssh url to rewrite ass https +

    :param url: (str) ssh url to rewrite ass https :param login: (str) authentication login name :param passwd: (str) authentication login password -:return (tuple) repo name and rewritten https url

    +:return (tuple) repo name and rewritten https url

    -Source code + +Expand source code +
    def rewrite_git_ssh_url_to_https(url, login=None, passwd=None):
         """
         :param url: (str) ssh url to rewrite ass https
    @@ -654,9 +676,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/highlighter/index.html b/docs/api/jumpscale/tools/highlighter/index.html index 4920d186f..71b242f78 100644 --- a/docs/api/jumpscale/tools/highlighter/index.html +++ b/docs/api/jumpscale/tools/highlighter/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.highlighter API documentation - - - - - + + + + + + +
    @@ -62,7 +64,9 @@

    Module jumpscale.tools.highlighter

    print_highlighted(C)
    -Source code + +Expand source code +
    '''Highlighter module helps with formatting text to be highlighted in terminal and in web
     
     example:
    @@ -73,7 +77,7 @@ 

    Module jumpscale.tools.highlighter

    def _init(self,**kwargs): self.lexers = Lexers() self.formatters = Formatters() - + def print_python(self,text,formatter="terminal"): C=Tools.text_replace(text) @@ -102,7 +106,7 @@

    Module jumpscale.tools.highlighter

    ports = [ 8001, 8001, 8002 ] connection_max = 5000 enabled = true - + """ print_toml(C) print_highlighted(C) @@ -127,7 +131,7 @@

    Module jumpscale.tools.highlighter

    def _init(self,**kwargs): self.lexers = Lexers() self.formatters = Formatters() - + def print_python(self,text,formatter="terminal"): C=Tools.text_replace(text) @@ -156,7 +160,7 @@

    Module jumpscale.tools.highlighter

    ports = [ 8001, 8001, 8002 ] connection_max = 5000 enabled = true - + """ print_toml(C) print_highlighted(C)
    @@ -170,36 +174,42 @@

    Module jumpscale.tools.highlighter

    Functions

    -def print_highlighted(txt, lexer=None, formatter='terminal') +def print_highlighted(txt, lexer=None, formatter='terminal')
    -
    +
    -Source code + +Expand source code +
    def print_highlighted(txt, lexer=None, formatter="terminal"):
         lexer = find_lexer_class_by_name(lexer) if lexer else guess_lexer(txt)
         print(pygments.highlight(txt, lexer, get_formatter_by_name(formatter)))
    -def print_python(txt, lexer=None, formatter='terminal') +def print_python(txt, lexer=None, formatter='terminal')
    -
    +
    -Source code + +Expand source code +
    def print_highlighted(txt, lexer=None, formatter="terminal"):
         lexer = find_lexer_class_by_name(lexer) if lexer else guess_lexer(txt)
         print(pygments.highlight(txt, lexer, get_formatter_by_name(formatter)))
    -def print_toml(txt, lexer=None, formatter='terminal') +def print_toml(txt, lexer=None, formatter='terminal')
    -
    +
    -Source code + +Expand source code +
    def print_highlighted(txt, lexer=None, formatter="terminal"):
         lexer = find_lexer_class_by_name(lexer) if lexer else guess_lexer(txt)
         print(pygments.highlight(txt, lexer, get_formatter_by_name(formatter)))
    @@ -209,15 +219,17 @@

    Functions

    def test()
    -
    +
    -Source code + +Expand source code +
    def test():
         C = """
         def _init(self,**kwargs):
             self.lexers = Lexers()
             self.formatters = Formatters()
    -        
    +
     
         def print_python(self,text,formatter="terminal"):
             C=Tools.text_replace(text)
    @@ -246,7 +258,7 @@ 

    Functions

    ports = [ 8001, 8001, 8002 ] connection_max = 5000 enabled = true - + """ print_toml(C) print_highlighted(C)
    @@ -280,9 +292,7 @@

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/http/index.html b/docs/api/jumpscale/tools/http/index.html index ab18659b7..f92a9b4f2 100644 --- a/docs/api/jumpscale/tools/http/index.html +++ b/docs/api/jumpscale/tools/http/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.http API documentation - - - - - + + + + + + +
    @@ -23,7 +25,9 @@

    Module jumpscale.tools.http

    This module wraps requests framework for http calls

    TODO: examples

    -Source code + +Expand source code +
    """This module wraps requests framework for http calls
     
     #TODO: examples
    @@ -58,9 +62,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/index.html b/docs/api/jumpscale/tools/index.html index 049874c2c..43b7cb62c 100644 --- a/docs/api/jumpscale/tools/index.html +++ b/docs/api/jumpscale/tools/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools API documentation - - - - - + + + + + + +
    @@ -26,64 +28,64 @@

    Sub-modules

    jumpscale.tools.alerthandler
    -
    +
    jumpscale.tools.codeloader
    -

    Loads a module from path …

    +

    Loads a module from path …

    jumpscale.tools.console
    -

    Console module helps with coloring in the console and asking for input from the user. +

    Console module helps with coloring in the console and asking for input from the user. ``` -JS-NG> …

    +JS-NG> …

    jumpscale.tools.depsresolver
    -
    +
    jumpscale.tools.errorhandler
    -
    +
    jumpscale.tools.git
    -

    Git module helps with everything around git management like pulling, cloning .. etc …

    +

    Git module helps with everything around git management like pulling, cloning .. etc …

    jumpscale.tools.highlighter
    -

    Highlighter module helps with formatting text to be highlighted in terminal and in web …

    +

    Highlighter module helps with formatting text to be highlighted in terminal and in web …

    jumpscale.tools.http
    -

    This module wraps requests framework for http calls …

    +

    This module wraps requests framework for http calls …

    jumpscale.tools.jinja2
    -

    This module helps with the common operations of jinja2 and reduces the boilerplate around it …

    +

    This module helps with the common operations of jinja2 and reduces the boilerplate around it …

    jumpscale.tools.keygen
    -
    +
    jumpscale.tools.schemac
    -

    schemac …

    +

    schemac …

    jumpscale.tools.startupcmd
    -
    +
    jumpscale.tools.syncer
    -

    Module to help syncing multiple machines with specific directories you have. +

    Module to help syncing multiple machines with specific directories you have. used in the jsync tool. ``` -JS-NG> xmonader = …

    +JS-NG> xmonader = …

    jumpscale.tools.timer
    -

    Helps with timing functions and see how long they took …

    +

    Helps with timing functions and see how long they took …

    @@ -127,9 +129,7 @@

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/jinja2/index.html b/docs/api/jumpscale/tools/jinja2/index.html index 2c220f495..4810fc8b4 100644 --- a/docs/api/jumpscale/tools/jinja2/index.html +++ b/docs/api/jumpscale/tools/jinja2/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.jinja2 API documentation - - - - - + + + + + + +
    @@ -22,7 +24,7 @@

    Module jumpscale.tools.jinja2

    This module helps with the common operations of jinja2 and reduces the boilerplate around it

    Getting environment

    -
    from jinja2 import (
    +
    from jinja2 import (
         Environment,
         FileSystemLoader,
         select_autoescape,
    @@ -38,7 +40,7 @@ 

    Getting a template from path or te

    Rendering a template with data

    you can render from a file path or a text directly using j.tools.jinja2.render_template and pass template_text in case of a string or template_path in case of a file path.

    e.g

    -
            data = dict(
    +
            data = dict(
                 generated_class_name=schema.url_to_class_name,
                 generated_properties=schema.props,
                 types_map=self._types_map,
    @@ -49,7 +51,9 @@ 

    Rendering a template with data

    return j.tools.jinja2.render_template(template_text=self._single_template, **data)
    -Source code + +Expand source code +
    """This module helps with the common operations of jinja2 and reduces the boilerplate around it
     
     
    @@ -184,7 +188,7 @@ 

    Functions

    def get_env(templates_path)
    -

    get an environment from templates root path

    +

    get an environment from templates root path

    Args

    templates_path : str
    @@ -192,11 +196,13 @@

    Args

    Returns

    -
    jinja2.Environment: Jinja2 env
    -
     
    -
    +
    jinja2.Environment
    +
    Jinja2 env
    +
    -Source code + +Expand source code +
    def get_env(templates_path):
         """get an environment from templates root path
     
    @@ -213,7 +219,7 @@ 

    Returns

    def get_template(template_path=None, template_text=None)
    -

    returns jinja2 template

    +

    returns jinja2 template

    Args

    template_path : str, optional
    @@ -223,18 +229,20 @@

    Args

    Raises

    -
    j.exceptions.Input: If both template_path and template_text are specified
    -
     
    -
    j.exceptions.Input: If no input was specified
    -
     
    +
    j.exceptions.Input
    +
    If both template_path and template_text are specified
    +
    j.exceptions.Input
    +
    If no input was specified

    Returns

    -
    jinja2.Template: Jinja2 template
    -
     
    -
    +
    jinja2.Template
    +
    Jinja2 template
    +
    -Source code + +Expand source code +
    def get_template(template_path=None, template_text=None):
         """returns jinja2 template
     
    @@ -265,9 +273,11 @@ 

    Returns

    def render_code_python(obj_key=None, template_path=None, template_text=None, dest=None, objForHash=None, name=None, **kwargs)
    -
    +
    -Source code + +Expand source code +
    def render_code_python(
         obj_key=None, template_path=None, template_text=None, dest=None, objForHash=None, name=None, **kwargs,
     ):
    @@ -279,7 +289,7 @@ 

    Returns

    def render_template(template_path=None, template_text=None, dest=None, **kwargs)
    -

    load the template if dest is specified will write in specified path, renders with specified kwargs

    +

    load the template if dest is specified will write in specified path, renders with specified kwargs

    Args

    template_path : str, optional
    @@ -291,16 +301,18 @@

    Args

    Raises

    -
    j.exceptions.Base: If rendering failed
    -
     
    +
    j.exceptions.Base
    +
    If rendering failed

    Returns

    -
    str
    +
    str
    Rendered template
    -
    +
    -Source code + +Expand source code +
    def render_template(template_path=None, template_text=None, dest=None, **kwargs):
         """load the template if dest is specified will write in specified path, renders with specified kwargs
     
    @@ -360,9 +372,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/keygen/index.html b/docs/api/jumpscale/tools/keygen/index.html index 077444136..a715d7102 100644 --- a/docs/api/jumpscale/tools/keygen/index.html +++ b/docs/api/jumpscale/tools/keygen/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.keygen API documentation - - - - - + + + + + + +
    @@ -26,7 +28,7 @@

    Sub-modules

    jumpscale.tools.keygen.keygen
    -
    +
    @@ -57,9 +59,7 @@

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/keygen/keygen.html b/docs/api/jumpscale/tools/keygen/keygen.html index 28f87786a..0134df051 100644 --- a/docs/api/jumpscale/tools/keygen/keygen.html +++ b/docs/api/jumpscale/tools/keygen/keygen.html @@ -3,15 +3,17 @@ - + jumpscale.tools.keygen.keygen API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.tools.keygen.keygen

    -Source code + +Expand source code +
    print("keygen..")
    @@ -49,9 +53,7 @@

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/schemac/compiler.html b/docs/api/jumpscale/tools/schemac/compiler.html index cb5555cde..337c0a7ed 100644 --- a/docs/api/jumpscale/tools/schemac/compiler.html +++ b/docs/api/jumpscale/tools/schemac/compiler.html @@ -3,15 +3,17 @@ - + jumpscale.tools.schemac.compiler API documentation - - - - - + + + + + + +
    @@ -22,7 +24,9 @@

    Module jumpscale.tools.schemac.compiler

    The compiler that parses JSX schema and generates a suitable backend in a supported lanaguage.

    -Source code + +Expand source code +
    """The compiler that parses JSX schema and generates a suitable backend in a supported lanaguage.
     
     """
    @@ -92,16 +96,18 @@ 

    Module jumpscale.tools.schemac.compiler

    Functions

    -def generator_by_name(language_name='python') +def generator_by_name(language_name='python')
    -

    Gets a generator by name

    +

    Gets a generator by name

    Keyword Arguments: language_name (str) – suitable generator (default: {"python"})

    Returns

    -

    (jumpscale.tools.schema.SchemaGenerator.Plugin) – The generator to use. Default

    +

    (jumpscale.tools.schema.SchemaGenerator.Plugin) – The generator to use. Default

    -Source code + +Expand source code +
    def generator_by_name(language_name="python"):
         """Gets a generator by name
     
    @@ -124,12 +130,14 @@ 

    Classes

    (lang='python', schema_text='')
    -

    Compiler class responsible for parsing schema_text and creating Parsed Schema objects with all metadata information needed.

    +

    Compiler class responsible for parsing schema_text and creating Parsed Schema objects with all metadata information needed.

    Keyword Arguments: lang (str)– language to generate for (default: {"python"}) -schema_text (str)– the schema text.

    +schema_text (str)– the schema text.

    -Source code + +Expand source code +
    class Compiler:
         def __init__(self, lang="python", schema_text=""):
             """Compiler class responsible for parsing schema_text and creating Parsed Schema objects with all metadata information needed.
    @@ -169,9 +177,11 @@ 

    Instance variables

    var generator
    -

    Gets generator by self.lang.

    +

    Gets generator by self.lang.

    -Source code + +Expand source code +
    @property
     def generator(self):
         """Gets generator by `self.lang`."""
    @@ -185,9 +195,11 @@ 

    Methods

    def parse(self)
    -

    Parses all the schemas in self._schema_text and returns Schema objects for generation

    +

    Parses all the schemas in self._schema_text and returns Schema objects for generation

    -Source code + +Expand source code +
    def parse(self):
         """Parses all the schemas in `self._schema_text` and returns Schema objects for generation"""
         schemas_texts = []
    @@ -243,9 +255,7 @@ 

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/schemac/index.html b/docs/api/jumpscale/tools/schemac/index.html index 77bbefb51..8123f226b 100644 --- a/docs/api/jumpscale/tools/schemac/index.html +++ b/docs/api/jumpscale/tools/schemac/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.schemac API documentation - - - - - + + + + + + +
    @@ -25,7 +27,7 @@

    schemac

    Example

    In this example there're bunch of types (bools, int, string, dict, time, date, enumerations, objects, list of objects, email) defined in jsx old style.

    jsx schema

    -
    schema = """
    +
    schema = """
     @url = despiegk.test
     listany = (LO)
     llist2 = "" (LS) #L means = list, S=String
    @@ -34,7 +36,7 @@ 

    jsx schema

    now = (T) info = (dict) theemail = (email) -status = "on,off" (E) +status = "on,off" (E) happy = "yes, no" (E) &nr = 4 obj = (O)!hamada.test @@ -59,7 +61,7 @@

    jsx schema

    Converting to the new system

    We expect that to expand or convert to plain old python classes, with dependency resolution.

    -
        c = j.tools.schemac.get_compiler(schema, "python")
    +
        c = j.tools.schemac.get_compiler(schema, "python")
         assert c
     
         assert c._schema_text
    @@ -70,7 +72,7 @@ 

    Converting to the new system

    print(generated_python)

    Generated file

    -
    #GENERATED CLASS DONT EDIT
    +
    #GENERATED CLASS DONT EDIT
     from jumpscale.core.base import Base, fields
     from enum import Enum
     
    @@ -121,9 +123,11 @@ 

    Generated file

    -Source code + +Expand source code +
    '''
    -# schemac 
    +# schemac
     
     Schemac is a tool used to convert (transpile) the schemas defined in jsx systems into the new objects definitions in js-ng
     
    @@ -143,7 +147,7 @@ 

    Generated file

    now = (T) info = (dict) theemail = (email) -status = "on,off" (E) +status = "on,off" (E) happy = "yes, no" (E) &nr = 4 obj = (O)!hamada.test @@ -251,11 +255,11 @@

    Sub-modules

    jumpscale.tools.schemac.compiler
    -

    The compiler that parses JSX schema and generates a suitable backend in a supported lanaguage.

    +

    The compiler that parses JSX schema and generates a suitable backend in a supported lanaguage.

    jumpscale.tools.schemac.plugins
    -
    +

    @@ -265,12 +269,14 @@

    Sub-modules

    Functions

    -def get_compiler(schema_text, lang='python') +def get_compiler(schema_text, lang='python')
    -
    +
    -Source code + +Expand source code +
    def get_compiler(schema_text, lang="python"):
         from .compiler import Compiler
     
    @@ -318,9 +324,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/schemac/plugins/crystal.html b/docs/api/jumpscale/tools/schemac/plugins/crystal.html index db0cd5b51..42e63cddf 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/crystal.html +++ b/docs/api/jumpscale/tools/schemac/plugins/crystal.html @@ -3,15 +3,17 @@ - + jumpscale.tools.schemac.plugins.crystal API documentation - - - - - + + + + + + +
    @@ -22,7 +24,9 @@

    Module jumpscale.tools.schemac.plugins.crystal

    Generator for crystal language. Takes in parsed schemas and generates crystal language classes.

    -Source code + +Expand source code +
    """Generator for crystal language. Takes in parsed schemas and generates crystal language classes.
     """
     from .plugin import Plugin
    @@ -126,9 +130,11 @@ 

    Functions

    def get_prop_line(prop)
    -
    +
    -Source code + +Expand source code +
    def get_prop_line(prop):
         prop_type = prop.prop_type
         crystal_type = types_map.get(prop_type)
    @@ -172,9 +178,11 @@ 

    Classes

    class CrystalGenerator
    -
    +
    -Source code + +Expand source code +
    class CrystalGenerator(Plugin):
         def __init__(self):
             super().__init__()
    @@ -227,9 +235,7 @@ 

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/schemac/plugins/index.html b/docs/api/jumpscale/tools/schemac/plugins/index.html index c5d631d5d..ea69666d9 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/index.html +++ b/docs/api/jumpscale/tools/schemac/plugins/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.schemac.plugins API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.tools.schemac.plugins

    -Source code + +Expand source code +
    from .jsng import JSNGGenerator
     from .crystal import CrystalGenerator
    @@ -31,19 +35,19 @@

    Sub-modules

    jumpscale.tools.schemac.plugins.crystal
    -

    Generator for crystal language. Takes in parsed schemas and generates crystal language classes.

    +

    Generator for crystal language. Takes in parsed schemas and generates crystal language classes.

    jumpscale.tools.schemac.plugins.jsng
    -

    Generator based on JS-NG fields. takes in parsed schemas and generates Python classes based on JS-NG fields.

    +

    Generator based on JS-NG fields. takes in parsed schemas and generates Python classes based on JS-NG fields.

    jumpscale.tools.schemac.plugins.plugin
    -
    +
    jumpscale.tools.schemac.plugins.utils
    -
    +
    @@ -77,9 +81,7 @@

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/schemac/plugins/jsng.html b/docs/api/jumpscale/tools/schemac/plugins/jsng.html index ce6590060..bcb66d431 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/jsng.html +++ b/docs/api/jumpscale/tools/schemac/plugins/jsng.html @@ -3,15 +3,17 @@ - + jumpscale.tools.schemac.plugins.jsng API documentation - - - - - + + + + + + +
    @@ -22,7 +24,9 @@

    Module jumpscale.tools.schemac.plugins.jsng

    Generator based on JS-NG fields. takes in parsed schemas and generates Python classes based on JS-NG fields.

    -Source code + +Expand source code +
    """Generator based on JS-NG fields. takes in parsed schemas and generates Python classes based on JS-NG fields.
     
     """
    @@ -148,9 +152,11 @@ 

    Functions

    def get_prop_line(prop)
    -
    +
    -Source code + +Expand source code +
    def get_prop_line(prop):
         prop_type = prop.prop_type
         if prop_type == "B":
    @@ -204,9 +210,11 @@ 

    Classes

    class JSNGGenerator
    -
    +
    -Source code + +Expand source code +
    class JSNGGenerator(Plugin):
         def __init__(self):
             super().__init__()
    @@ -259,9 +267,7 @@ 

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/schemac/plugins/plugin.html b/docs/api/jumpscale/tools/schemac/plugins/plugin.html index 440c04964..ffe958f50 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/plugin.html +++ b/docs/api/jumpscale/tools/schemac/plugins/plugin.html @@ -3,15 +3,17 @@ - + jumpscale.tools.schemac.plugins.plugin API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.tools.schemac.plugins.plugin

    -Source code + +Expand source code +
    from jumpscale.loader import j
     
     
    @@ -91,12 +95,13 @@ 

    Classes

    class Plugin -(*args, **kwargs)
    -
    +
    -Source code + +Expand source code +
    class Plugin:
         def _generate_single(self, schema):
             """Generates a single schema template
    @@ -153,8 +158,8 @@ 

    Classes

    Subclasses

    Methods

    @@ -162,13 +167,15 @@

    Methods

    def generate(self, parsed_schemas)
    -

    Generates a string has all the enumerations and classes in the target language.

    +

    Generates a string has all the enumerations and classes in the target language.

    Arguments

    parsed_schemas (Dict[str, jumpscale.data.Schema]) – a dict of schema name to schema object

    Returns

    -

    (str) – Compiled string of all enumerations and classes in the target language.

    +

    (str) – Compiled string of all enumerations and classes in the target language.

    -Source code + +Expand source code +
    def generate(self, parsed_schemas):
         """Generates a string has all the enumerations and classes in the target language.
     
    @@ -234,9 +241,7 @@ 

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/schemac/plugins/utils.html b/docs/api/jumpscale/tools/schemac/plugins/utils.html index 442bc56a6..5a1008fa5 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/utils.html +++ b/docs/api/jumpscale/tools/schemac/plugins/utils.html @@ -3,15 +3,17 @@ - + jumpscale.tools.schemac.plugins.utils API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.tools.schemac.plugins.utils

    -Source code + +Expand source code +
    def convert_url_to_class_name(url):
         urlparts = url.split(".")
         return "".join(x.capitalize() for x in urlparts)
    @@ -38,9 +42,11 @@

    Functions

    def convert_url_to_class_name(url)
    -
    +
    -Source code + +Expand source code +
    def convert_url_to_class_name(url):
         urlparts = url.split(".")
         return "".join(x.capitalize() for x in urlparts)
    @@ -71,9 +77,7 @@

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/startupcmd/index.html b/docs/api/jumpscale/tools/startupcmd/index.html index 1f31d66be..e64ec1a88 100644 --- a/docs/api/jumpscale/tools/startupcmd/index.html +++ b/docs/api/jumpscale/tools/startupcmd/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.startupcmd API documentation - - - - - + + + + + + +
    @@ -21,7 +23,9 @@

    Module jumpscale.tools.startupcmd

    -Source code + +Expand source code +
    def export_module_as():
         from jumpscale.core.base import StoredFactory
         from .startupcmd import StartupCmd
    @@ -34,7 +38,7 @@ 

    Sub-modules

    jumpscale.tools.startupcmd.startupcmd
    -

    This module manages long running commands …

    +

    This module manages long running commands …

    @@ -47,9 +51,11 @@

    Functions

    def export_module_as()
    -
    +
    -Source code + +Expand source code +
    def export_module_as():
         from jumpscale.core.base import StoredFactory
         from .startupcmd import StartupCmd
    @@ -87,9 +93,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/startupcmd/startupcmd.html b/docs/api/jumpscale/tools/startupcmd/startupcmd.html index 60ebfe06a..d826eacfb 100644 --- a/docs/api/jumpscale/tools/startupcmd/startupcmd.html +++ b/docs/api/jumpscale/tools/startupcmd/startupcmd.html @@ -3,15 +3,17 @@ - + jumpscale.tools.startupcmd.startupcmd API documentation - - - - - + + + + + + +
    @@ -42,7 +44,9 @@

    Module jumpscale.tools.startupcmd.startupcmd

    you can add cmd.ports, cmd.process_strings_regex or cmd.process_strings_regex to reach the process pid

    -Source code + +Expand source code +
    """This module manages long running commands
     
     To start a python http server
    @@ -370,12 +374,14 @@ 

    Classes

    class Executor -(*args, **kwargs) +(value, names=None, *, module=None, qualname=None, type=None, start=1)
    -

    An enumeration.

    +

    An enumeration.

    -Source code + +Expand source code +
    class Executor(Enum):
         TMUX = "tmux"
         FOREGROUND = "foreground"
    @@ -388,11 +394,11 @@

    Class variables

    var FOREGROUND
    -
    +
    var TMUX
    -
    +
    @@ -401,11 +407,11 @@

    Class variables

    (*args, **kwargs)
    -

    A simple attribute-based namespace.

    +

    A simple attribute-based namespace.

    SimpleNamespace(**kwargs)

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    +
    class Person(Base):
         name = fields.String()
         age = fields.Float()
     
    @@ -420,9 +426,11 @@ 

    Args

    instance name. Defaults to None.
    **values
    any given field values to initiate the instance with
    -

    +
    -Source code + +Expand source code +
    class StartupCmd(Base):
         start_cmd = fields.String()
         ports = fields.List(fields.Integer())
    @@ -704,16 +712,18 @@ 

    Instance variables

    var check_cmd
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -729,9 +739,11 @@ 

    Returns

    var cmd_path
    -
    +
    -Source code + +Expand source code +
    @property
     def cmd_path(self):
         if not self._cmd_path:
    @@ -742,16 +754,18 @@ 

    Returns

    var env
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -767,16 +781,18 @@ 

    Returns

    var executor
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -792,16 +808,18 @@ 

    Returns

    var path
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -817,9 +835,11 @@ 

    Returns

    var pid
    -
    +
    -Source code + +Expand source code +
    @property
     def pid(self):
         if not self._pid:
    @@ -831,16 +851,18 @@ 

    Returns

    var ports
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -856,9 +878,11 @@ 

    Returns

    var process
    -
    +
    -Source code + +Expand source code +
    @property
     def process(self):
         if not self._process:
    @@ -875,16 +899,18 @@ 

    Returns

    var process_strings
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -900,16 +926,18 @@ 

    Returns

    var process_strings_regex
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -925,16 +953,18 @@ 

    Returns

    var start_cmd
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -950,16 +980,18 @@ 

    Returns

    var stop_cmd
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -975,16 +1007,18 @@ 

    Returns

    var timeout
    -

    getter method this property

    +

    getter method this property

    will call _get_value, which would if the value is already defined and will get the default value if not

    Returns

    -
    any
    +
    any
    the field value
    -
    +
    -Source code + +Expand source code +
    def getter(self):
         """
         getter method this property
    @@ -1005,14 +1039,16 @@ 

    Methods

    def is_running(self)
    -

    Checks if startup cmd is running. Will use check_cmd property if defined or check based on objet properties

    +

    Checks if startup cmd is running. Will use check_cmd property if defined or check based on objet properties

    Returns

    -
    bool
    +
    bool
    True if it is running
    -
    +
    -Source code + +Expand source code +
    def is_running(self):
         """Checks if startup cmd is running. Will use `check_cmd` property if defined or check based on objet properties
     
    @@ -1033,9 +1069,11 @@ 

    Returns

    def reset(self)
    -
    +
    -Source code + +Expand source code +
    def reset(self):
         self._process = None
         self._pid = None
    @@ -1045,9 +1083,11 @@

    Returns

    def start(self)
    -

    Starts the process

    +

    Starts the process

    -Source code + +Expand source code +
    def start(self):
         """Starts the process
         """
    @@ -1095,7 +1135,7 @@ 

    Returns

    def stop(self, force=True, wait_for_stop=True, die=True, timeout=None)
    -

    Stops the running command

    +

    Stops the running command

    Args

    force : bool, optional
    @@ -1106,9 +1146,11 @@

    Args

    If True will raise if timeout is exceeded for stop. Defaults to True.
    timeout : int, optional
    Timeout for stop wait.If not set will use timeout property. Defaults to None.
    -
    +
    -Source code + +Expand source code +
    def stop(self, force=True, wait_for_stop=True, die=True, timeout=None):
         """Stops the running command
     
    @@ -1137,7 +1179,7 @@ 

    Args

    def wait_for_running(self, die=True, timeout=10)
    -

    Wait for start to finishes

    +

    Wait for start to finishes

    Args

    die : bool, optional
    @@ -1146,9 +1188,14 @@

    Args

    Timeout for wait operation. Defaults to None.

    Raises

    -

    j.exceptions.Timeout: If timeout is exceeded.

    +
    +
    j.exceptions.Timeout
    +
    If timeout is exceeded.
    +
    -Source code + +Expand source code +
    def wait_for_running(self, die=True, timeout=10):
         """Wait for start to finishes
     
    @@ -1166,7 +1213,7 @@ 

    Raises

    def wait_for_stop(self, die=True, timeout=10)
    -

    Wait for stop to finishes

    +

    Wait for stop to finishes

    Args

    die : bool, optional
    @@ -1175,9 +1222,14 @@

    Args

    Timeout for wait operation. Defaults to None.

    Raises

    -

    j.exceptions.Timeout: If timeout is exceeded.

    +
    +
    j.exceptions.Timeout
    +
    If timeout is exceeded.
    +
    -Source code + +Expand source code +
    def wait_for_stop(self, die=True, timeout=10):
         """Wait for stop to finishes
     
    @@ -1256,9 +1308,7 @@ 

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/syncer/index.html b/docs/api/jumpscale/tools/syncer/index.html index 9faccc119..56704ff96 100644 --- a/docs/api/jumpscale/tools/syncer/index.html +++ b/docs/api/jumpscale/tools/syncer/index.html @@ -3,18 +3,20 @@ - + jumpscale.tools.syncer API documentation - - - - - + + + + + + +
    @@ -36,7 +38,9 @@

    Module jumpscale.tools.syncer

    2019-09-03T11:38:47.183394+0200 - paths: {'/home/xmonader/wspace/tfchain-py': '/tmp/tfchain-py'}

    -Source code + +Expand source code +
    """Module to help syncing multiple machines with specific directories you have.
     used in the jsync tool.
     ```
    @@ -281,27 +285,26 @@ 

    Classes

    class Syncer -(sshclients_names, paths, patterns=None, ignore_patterns=None, ignore_directories=False, case_sensitive=True) +(sshclients_names: List[str], paths: Dict[str, str], patterns: Optional[List[str]] = None, ignore_patterns: Optional[List[str]] = None, ignore_directories: Optional[List[str]] = False, case_sensitive: bool = True)
    -

    Matches given patterns with file paths associated with occurring events.

    +

    Matches given patterns with file paths associated with occurring events.

    Creates new syncer tool

    Arguments

    sshclients_names {List[str]} – list of sshclient names -paths {Dict[str, str]} – paths to watch src/dest form of dict {'/tmp/myproj':'/root/proj'} -Keyword Arguments: +paths {Dict[str, str]} – paths to watch src/dest form of dict {'/tmp/myproj':'/root/proj'}

    +

    Keyword Arguments: patterns {Optional[List[str]]} – optional list of patterns to watch (default: {None}) ignore_patterns {Optional[List[str]]} – patterns to ignore, e.g .git, pycache (default: {None}) ignore_directories {Optional[List[str]]} – directories to ignore (default: {False}) case_sensitive {bool} – case sensitive watching (default: {True})

    Returns

    -
    -
    SyncerSyncer object
    -
     
    -
    +

    Syncer – Syncer object

    -Source code + +Expand source code +
    class Syncer(PatternMatchingEventHandler):
         def __init__(
             self,
    @@ -521,13 +524,15 @@ 

    Methods

    def on_created(self, event)
    -

    Called when a file or directory is created.

    +

    Called when a file or directory is created.

    :param event: Event representing file/directory creation. :type event: -:class:DirCreatedEvent or :class:FileCreatedEvent

    +:class:DirCreatedEvent or :class:FileCreatedEvent

    -Source code + +Expand source code +
    def on_created(self, event):
         super().on_created(event)
         what = "directory" if event.is_directory else "file"
    @@ -552,13 +557,15 @@ 

    Methods

    def on_deleted(self, event)
    -

    Called when a file or directory is deleted.

    +

    Called when a file or directory is deleted.

    :param event: Event representing file/directory deletion. :type event: -:class:DirDeletedEvent or :class:FileDeletedEvent

    +:class:DirDeletedEvent or :class:FileDeletedEvent

    -Source code + +Expand source code +
    def on_deleted(self, event):
         super().on_deleted(event)
     
    @@ -581,13 +588,15 @@ 

    Methods

    def on_modified(self, event)
    -

    Called when a file or directory is modified.

    +

    Called when a file or directory is modified.

    :param event: Event representing file/directory modification. :type event: -:class:DirModifiedEvent or :class:FileModifiedEvent

    +:class:DirModifiedEvent or :class:FileModifiedEvent

    -Source code + +Expand source code +
    def on_modified(self, event):
         super().on_modified(event)
         what = "directory" if event.is_directory else "file"
    @@ -610,13 +619,15 @@ 

    Methods

    def on_moved(self, event)
    -

    Called when a file or a directory is moved or renamed.

    +

    Called when a file or a directory is moved or renamed.

    :param event: Event representing file/directory movement. :type event: -:class:DirMovedEvent or :class:FileMovedEvent

    +:class:DirMovedEvent or :class:FileMovedEvent

    -Source code + +Expand source code +
    def on_moved(self, event):
         super().on_moved(event)
     
    @@ -642,11 +653,13 @@ 

    Methods

    def start(self, sync=True)
    -

    Start syncing/watching paths to remote machines

    +

    Start syncing/watching paths to remote machines

    Keyword Arguments: -sync {bool} – sync dirs/files first (default: {True})

    +sync {bool} – sync dirs/files first (default: {True})

    -Source code + +Expand source code +
    def start(self, sync=True):
         """Start syncing/watching paths to remote machines
     
    @@ -673,9 +686,11 @@ 

    Methods

    def sync(self)
    -

    Sync directory structure and files

    +

    Sync directory structure and files

    -Source code + +Expand source code +
    def sync(self):
         """Sync directory structure and files
     
    @@ -752,9 +767,7 @@ 

    -

    Generated by pdoc 0.6.4.

    +

    Generated by pdoc 0.10.0.

    - - - \ No newline at end of file + diff --git a/docs/api/jumpscale/tools/timer/index.html b/docs/api/jumpscale/tools/timer/index.html index f953c4b74..460aa9259 100644 --- a/docs/api/jumpscale/tools/timer/index.html +++ b/docs/api/jumpscale/tools/timer/index.html @@ -3,15 +3,17 @@ - + jumpscale.tools.timer API documentation - - - - - + + + + + + +
    @@ -46,7 +48,9 @@

    Module jumpscale.tools.timer

    120

    -Source code + +Expand source code +
    """Helps with timing functions and see how long they took
     
     example
    @@ -103,9 +107,11 @@ 

    Functions

    def timeit(func)
    -
    +
    -Source code + +Expand source code +
    def timeit(func):
         def wrapper(*args, **kwargs):
             start_time = time.time()
    @@ -143,9 +149,7 @@ 

    Index

    - - - \ No newline at end of file + diff --git a/poetry.lock b/poetry.lock index 5e8ac9f97..87748e819 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,14 +6,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "argh" -version = "0.26.2" -description = "An unobtrusive argparse wrapper with natural syntax" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "arrow" version = "0.15.8" @@ -677,21 +669,13 @@ category = "dev" optional = false python-versions = ">=3.7" -[[package]] -name = "pathtools" -version = "0.1.2" -description = "File system general utilities" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "pdoc3" -version = "0.6.4" +version = "0.10.0" description = "Auto-generate API documentation for Python projects." category = "main" optional = false -python-versions = ">= 3.5" +python-versions = ">= 3.6" [package.dependencies] mako = "*" @@ -1108,16 +1092,14 @@ python-versions = "*" [[package]] name = "watchdog" -version = "0.9.0" +version = "2.1.9" description = "Filesystem events monitoring" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" -[package.dependencies] -argh = ">=0.24.1" -pathtools = ">=0.1.1" -PyYAML = ">=3.10" +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "wcwidth" @@ -1205,17 +1187,13 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = ">=3.7,<4.0" -content-hash = "b24aea3b56d8c2a0a08f57ac5ead41759296250bf44dd36b65e52751afc38e9d" +content-hash = "82a17f140463e8075bf0a6ef1d0dd1090aaab462bdbf8c019d820ac552720568" [metadata.files] appnope = [ {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, ] -argh = [ - {file = "argh-0.26.2-py2.py3-none-any.whl", hash = "sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3"}, - {file = "argh-0.26.2.tar.gz", hash = "sha256:e9535b8c84dc9571a48999094fda7f33e63c3f1b74f3e5f3ac0105a58405bb65"}, -] arrow = [ {file = "arrow-0.15.8-py2.py3-none-any.whl", hash = "sha256:271b8e05174d48e50324ed0dc5d74796c839c7e579a4f21cf1a7394665f9e94f"}, {file = "arrow-0.15.8.tar.gz", hash = "sha256:edc31dc051db12c95da9bac0271cd1027b8e36912daf6d4580af53b23e62721a"}, @@ -1729,11 +1707,9 @@ pathspec = [ {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, ] -pathtools = [ - {file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"}, -] pdoc3 = [ - {file = "pdoc3-0.6.4.tar.gz", hash = "sha256:85cbb0de17d1306157d19b08b67ad84817098c12ad9f92ec203b79d0307b6a25"}, + {file = "pdoc3-0.10.0-py3-none-any.whl", hash = "sha256:ba45d1ada1bd987427d2bf5cdec30b2631a3ff5fb01f6d0e77648a572ce6028b"}, + {file = "pdoc3-0.10.0.tar.gz", hash = "sha256:5f22e7bcb969006738e1aa4219c75a32f34c2d62d46dc9d2fb2d3e0b0287e4b7"}, ] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, @@ -1987,7 +1963,31 @@ urwid = [ {file = "urwid-2.1.2.tar.gz", hash = "sha256:588bee9c1cb208d0906a9f73c613d2bd32c3ed3702012f51efe318a3f2127eae"}, ] watchdog = [ - {file = "watchdog-0.9.0.tar.gz", hash = "sha256:965f658d0732de3188211932aeb0bb457587f04f63ab4c1e33eab878e9de961d"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658"}, + {file = "watchdog-2.1.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591"}, + {file = "watchdog-2.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd"}, + {file = "watchdog-2.1.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3"}, + {file = "watchdog-2.1.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d"}, + {file = "watchdog-2.1.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_armv7l.whl", hash = "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_i686.whl", hash = "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64.whl", hash = "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_s390x.whl", hash = "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6"}, + {file = "watchdog-2.1.9-py3-none-win32.whl", hash = "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1"}, + {file = "watchdog-2.1.9-py3-none-win_amd64.whl", hash = "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c"}, + {file = "watchdog-2.1.9-py3-none-win_ia64.whl", hash = "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428"}, + {file = "watchdog-2.1.9.tar.gz", hash = "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, diff --git a/pyproject.toml b/pyproject.toml index f68b12fa9..9ac47f22b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ license = "MIT" [tool.poetry.dependencies] python = ">=3.7,<4.0" -pdoc3 = "^0.6.3" +pdoc3 = "^0.10.0" pytoml = "^0.1.21" secretconf = "^0.1.2" better-exceptions = "^0.2.2" @@ -30,7 +30,7 @@ distro = "^1.4" libtmux = "^0.8.2" redis = "^3.3" dill = "^0.3.0" -watchdog = "^0.9.0" +watchdog = "^2.1.9" GitPython = "^3.0" docker = "^4.2.0" Jinja2 = "^3.1.2" From b4f1b5bf32cfca93b1b062e5784bf8e137a1dbb3 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ghanem Date: Fri, 28 Oct 2022 04:42:11 +0200 Subject: [PATCH 4/8] Remove threesdk and update docker file (#611) * update pre-commit deps * remove explorer related config * remove threesdk and usershell * remove 3sdk docs * remove usershell entrypoint and update docs * regenerate api docs * update black fersion in workflow --- .github/workflows/black-docstr.yml | 2 +- .pre-commit-config.yaml | 4 +- README.md | 2 +- .../clients/currencylayer/currencies.html | 232 -- .../clients/currencylayer/currencylayer.html | 454 --- .../clients/currencylayer/index.html | 1519 --------- .../clients/digitalocean/digitalocean.html | 2822 ----------------- .../jumpscale/clients/digitalocean/index.html | 104 - .../clients/digitalocean/project.html | 665 ---- docs/api/jumpscale/clients/gdrive/gdrive.html | 308 -- docs/api/jumpscale/clients/gdrive/index.html | 97 - docs/api/jumpscale/clients/gedis/gedis.html | 721 ----- docs/api/jumpscale/clients/gedis/index.html | 238 -- docs/api/jumpscale/clients/github/base.html | 234 -- docs/api/jumpscale/clients/github/github.html | 462 --- docs/api/jumpscale/clients/github/index.html | 115 - docs/api/jumpscale/clients/github/issue.html | 1090 ------- .../jumpscale/clients/github/milestone.html | 368 --- docs/api/jumpscale/clients/github/repo.html | 1525 --------- docs/api/jumpscale/clients/gogs/gogs.html | 323 -- docs/api/jumpscale/clients/gogs/index.html | 95 - docs/api/jumpscale/clients/mail/index.html | 97 - docs/api/jumpscale/clients/mail/mail.html | 575 ---- docs/api/jumpscale/clients/s3/index.html | 95 - docs/api/jumpscale/clients/s3/s3.html | 719 ----- .../api/jumpscale/clients/sendgrid/index.html | 95 - .../jumpscale/clients/sendgrid/sendgrid.html | 292 -- docs/api/jumpscale/clients/sonic/client.html | 293 -- docs/api/jumpscale/clients/sonic/index.html | 95 - .../jumpscale/clients/syncthing/index.html | 97 - .../clients/syncthing/syncthing.html | 789 ----- docs/api/jumpscale/clients/trello/index.html | 97 - docs/api/jumpscale/clients/trello/trello.html | 319 -- docs/api/jumpscale/clients/twilio/index.html | 95 - docs/api/jumpscale/clients/twilio/twilio.html | 290 -- .../api/jumpscale/clients/zerotier/index.html | 97 - .../jumpscale/clients/zerotier/zerotier.html | 1048 ------ docs/api/jumpscale/core/base/store.html | 1941 ------------ docs/api/jumpscale/core/config.html | 233 -- docs/api/jumpscale/core/config/config.html | 32 +- docs/api/jumpscale/core/exceptions.html | 85 - docs/api/jumpscale/core/logging.html | 80 - docs/api/jumpscale/data/countries/index.html | 135 - .../jumpscale/data/encryption/encryption.html | 269 -- docs/api/jumpscale/data/hash/HashTool.py | 0 docs/api/jumpscale/data/hash/__init__.py | 0 .../jumpscale/data/randomnames/generate.html | 256 -- .../api/jumpscale/data/randomnames/index.html | 69 - .../data/serializers/serializers.html | 59 - .../data/terminaltable/terminaltable.html | 95 - .../data/treemanager/exceptions.html | 139 - .../api/jumpscale/data/treemanager/index.html | 78 - .../data/treemanager/treemanager.html | 1189 ------- docs/api/jumpscale/entry_points/index.html | 5 - .../api/jumpscale/entry_points/usershell.html | 828 ----- docs/api/jumpscale/god.html | 803 ----- docs/api/jumpscale/index.html | 5 - docs/api/jumpscale/sals/fs/fs.html | 2503 --------------- docs/api/jumpscale/sals/fs/fs2.html | 1030 ------ .../jumpscale/sals/hostsfile/hostsfile.html | 363 --- docs/api/jumpscale/sals/process/process.html | 53 - .../jumpscale/servers/gedis/baseactor.html | 333 -- .../servers/gedis/example_actor.html | 475 --- .../servers/gedis/example_greeter.html | 257 -- docs/api/jumpscale/servers/gedis/index.html | 210 -- docs/api/jumpscale/servers/gedis/server.html | 1068 ------- .../jumpscale/servers/gedis/systemactor.html | 382 --- .../jumpscale/servers/gedis_http/index.html | 485 --- docs/api/jumpscale/threesdk/container.html | 385 --- .../jumpscale/threesdk/identitymanager.html | 531 ---- docs/api/jumpscale/threesdk/index.html | 518 --- docs/api/jumpscale/threesdk/settings.html | 59 - docs/api/jumpscale/tools/console/console.html | 689 ---- docs/api/jumpscale/tools/dnstool/index.html | 197 -- .../jumpscale/tools/imagelib/imagelib.html | 302 -- docs/api/jumpscale/tools/imagelib/index.html | 79 - .../jumpscale/tools/poolexecutor/index.html | 87 - .../tools/poolexecutor/poolexecutor.html | 273 -- docs/api/jumpscale/tools/profiler/index.html | 206 -- docs/api/jumpscale/tools/tfgateway/index.html | 409 --- docs/api/jumpscale/tools/timer/timer.html | 97 - docs/wiki/3sdk.md | 69 - docs/wiki/README.md | 6 +- docs/wiki/_sidebar.md | 4 +- docs/wiki/docker.md | 16 - ...d => loader_object_namespaces_concepts.md} | 7 +- docs/wiki/specs.md | 4 +- docs/wiki/tutorials/developing_sal.md | 4 +- jumpscale/core/config/config.py | 16 +- jumpscale/entry_points/usershell.py | 247 -- jumpscale/threesdk/__init__.py | 76 - jumpscale/threesdk/container.py | 76 - jumpscale/threesdk/identitymanager.py | 161 - jumpscale/threesdk/settings.py | 1 - jumpscale/threesdk/threebot.py | 114 - pyproject.toml | 1 - 96 files changed, 46 insertions(+), 34521 deletions(-) delete mode 100644 docs/api/jumpscale/clients/currencylayer/currencies.html delete mode 100644 docs/api/jumpscale/clients/currencylayer/currencylayer.html delete mode 100644 docs/api/jumpscale/clients/currencylayer/index.html delete mode 100644 docs/api/jumpscale/clients/digitalocean/digitalocean.html delete mode 100644 docs/api/jumpscale/clients/digitalocean/index.html delete mode 100644 docs/api/jumpscale/clients/digitalocean/project.html delete mode 100644 docs/api/jumpscale/clients/gdrive/gdrive.html delete mode 100644 docs/api/jumpscale/clients/gdrive/index.html delete mode 100644 docs/api/jumpscale/clients/gedis/gedis.html delete mode 100644 docs/api/jumpscale/clients/gedis/index.html delete mode 100644 docs/api/jumpscale/clients/github/base.html delete mode 100644 docs/api/jumpscale/clients/github/github.html delete mode 100644 docs/api/jumpscale/clients/github/index.html delete mode 100644 docs/api/jumpscale/clients/github/issue.html delete mode 100644 docs/api/jumpscale/clients/github/milestone.html delete mode 100644 docs/api/jumpscale/clients/github/repo.html delete mode 100644 docs/api/jumpscale/clients/gogs/gogs.html delete mode 100644 docs/api/jumpscale/clients/gogs/index.html delete mode 100644 docs/api/jumpscale/clients/mail/index.html delete mode 100644 docs/api/jumpscale/clients/mail/mail.html delete mode 100644 docs/api/jumpscale/clients/s3/index.html delete mode 100644 docs/api/jumpscale/clients/s3/s3.html delete mode 100644 docs/api/jumpscale/clients/sendgrid/index.html delete mode 100644 docs/api/jumpscale/clients/sendgrid/sendgrid.html delete mode 100644 docs/api/jumpscale/clients/sonic/client.html delete mode 100644 docs/api/jumpscale/clients/sonic/index.html delete mode 100644 docs/api/jumpscale/clients/syncthing/index.html delete mode 100644 docs/api/jumpscale/clients/syncthing/syncthing.html delete mode 100644 docs/api/jumpscale/clients/trello/index.html delete mode 100644 docs/api/jumpscale/clients/trello/trello.html delete mode 100644 docs/api/jumpscale/clients/twilio/index.html delete mode 100644 docs/api/jumpscale/clients/twilio/twilio.html delete mode 100644 docs/api/jumpscale/clients/zerotier/index.html delete mode 100644 docs/api/jumpscale/clients/zerotier/zerotier.html delete mode 100644 docs/api/jumpscale/core/base/store.html delete mode 100644 docs/api/jumpscale/core/config.html delete mode 100644 docs/api/jumpscale/core/exceptions.html delete mode 100644 docs/api/jumpscale/core/logging.html delete mode 100644 docs/api/jumpscale/data/countries/index.html delete mode 100644 docs/api/jumpscale/data/encryption/encryption.html delete mode 100644 docs/api/jumpscale/data/hash/HashTool.py delete mode 100644 docs/api/jumpscale/data/hash/__init__.py delete mode 100644 docs/api/jumpscale/data/randomnames/generate.html delete mode 100644 docs/api/jumpscale/data/randomnames/index.html delete mode 100644 docs/api/jumpscale/data/serializers/serializers.html delete mode 100644 docs/api/jumpscale/data/terminaltable/terminaltable.html delete mode 100644 docs/api/jumpscale/data/treemanager/exceptions.html delete mode 100644 docs/api/jumpscale/data/treemanager/index.html delete mode 100644 docs/api/jumpscale/data/treemanager/treemanager.html delete mode 100644 docs/api/jumpscale/entry_points/usershell.html delete mode 100644 docs/api/jumpscale/god.html delete mode 100644 docs/api/jumpscale/sals/fs/fs.html delete mode 100644 docs/api/jumpscale/sals/fs/fs2.html delete mode 100644 docs/api/jumpscale/sals/hostsfile/hostsfile.html delete mode 100644 docs/api/jumpscale/sals/process/process.html delete mode 100644 docs/api/jumpscale/servers/gedis/baseactor.html delete mode 100644 docs/api/jumpscale/servers/gedis/example_actor.html delete mode 100644 docs/api/jumpscale/servers/gedis/example_greeter.html delete mode 100644 docs/api/jumpscale/servers/gedis/index.html delete mode 100644 docs/api/jumpscale/servers/gedis/server.html delete mode 100644 docs/api/jumpscale/servers/gedis/systemactor.html delete mode 100644 docs/api/jumpscale/servers/gedis_http/index.html delete mode 100644 docs/api/jumpscale/threesdk/container.html delete mode 100644 docs/api/jumpscale/threesdk/identitymanager.html delete mode 100644 docs/api/jumpscale/threesdk/index.html delete mode 100644 docs/api/jumpscale/threesdk/settings.html delete mode 100644 docs/api/jumpscale/tools/console/console.html delete mode 100644 docs/api/jumpscale/tools/dnstool/index.html delete mode 100644 docs/api/jumpscale/tools/imagelib/imagelib.html delete mode 100644 docs/api/jumpscale/tools/imagelib/index.html delete mode 100644 docs/api/jumpscale/tools/poolexecutor/index.html delete mode 100644 docs/api/jumpscale/tools/poolexecutor/poolexecutor.html delete mode 100644 docs/api/jumpscale/tools/profiler/index.html delete mode 100644 docs/api/jumpscale/tools/tfgateway/index.html delete mode 100644 docs/api/jumpscale/tools/timer/timer.html delete mode 100644 docs/wiki/3sdk.md delete mode 100644 docs/wiki/docker.md rename docs/wiki/{god_object_namespaces_concepts.md => loader_object_namespaces_concepts.md} (97%) delete mode 100644 jumpscale/entry_points/usershell.py delete mode 100755 jumpscale/threesdk/__init__.py delete mode 100644 jumpscale/threesdk/container.py delete mode 100644 jumpscale/threesdk/identitymanager.py delete mode 100644 jumpscale/threesdk/settings.py delete mode 100644 jumpscale/threesdk/threebot.py diff --git a/.github/workflows/black-docstr.yml b/.github/workflows/black-docstr.yml index 3c7a6b382..0718a0bf2 100644 --- a/.github/workflows/black-docstr.yml +++ b/.github/workflows/black-docstr.yml @@ -14,7 +14,7 @@ jobs: run: | sudo apt-get update sudo apt-get install git python3-pip -y - sudo pip3 install black==19.10b0 docstr-coverage + sudo pip3 install black==22.10.0 docstr-coverage FILES="$(git diff --name-only --diff-filter=A --diff-filter=M "origin/${{github.base_ref}}" '*.py')" black --check -l 120 -t py37 --exclude 'templates' $FILES for FILE in $FILES; do echo $FILE; docstr-coverage $FILE || true; done diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f93ad3945..a3961717d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,14 +3,14 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.4.0 + rev: v4.3.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - id: check-merge-conflict - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 22.10.0 hooks: - id: black args: [--line-length=120, --target-version=py37] diff --git a/README.md b/README.md index f7f6229aa..ba5f0bc20 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ config management/automation framework ## Principles - pip installable -- facilities exposed under `god object`: `j` +- facilities exposed under `loader object`: `j` - pluggable - docs and tests are as important as code diff --git a/docs/api/jumpscale/clients/currencylayer/currencies.html b/docs/api/jumpscale/clients/currencylayer/currencies.html deleted file mode 100644 index 7456a57d9..000000000 --- a/docs/api/jumpscale/clients/currencylayer/currencies.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - -jumpscale.clients.currencylayer.currencies API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.currencylayer.currencies

    -
    -
    -
    -Source code -
    CURRENCIES = {
    -    "aed": 3.672798,
    -    "afn": 71.150002,
    -    "all": 104.900002,
    -    "amd": 482.799988,
    -    "ang": 1.789542,
    -    "aoa": 238.544006,
    -    "ars": 24.896023,
    -    "aud": 1.306702,
    -    "awg": 1.78,
    -    "azn": 1.699505,
    -    "bam": 1.656031,
    -    "bbd": 2,
    -    "bdt": 84.099998,
    -    "bgn": 1.6624,
    -    "bhd": 0.377503,
    -    "bif": 1750.97998,
    -    "bmd": 1,
    -    "bnd": 1.321296,
    -    "bob": 6.859653,
    -    "brl": 3.846501,
    -    "bsd": 1,
    -    "btc": 0.00013,
    -    "btn": 66.925003,
    -    "bwp": 9.995016,
    -    "byn": 2.000042,
    -    "byr": 19600,
    -    "bzd": 1.997802,
    -    "cad": 1.29528,
    -    "cdf": 1565.499204,
    -    "chf": 0.98171,
    -    "clf": 0.02292,
    -    "clp": 627.700012,
    -    "cny": 6.392502,
    -    "cop": 2828,
    -    "crc": 564.749842,
    -    "cuc": 1,
    -    "cup": 26.5,
    -    "cve": 93.239998,
    -    "czk": 21.658998,
    -    "djf": 177.498647,
    -    "dkk": 6.29685,
    -    "dop": 49.519768,
    -    "dzd": 115.929789,
    -    "egp": 17.809999,
    -    "ern": 14.989959,
    -    "etb": 27.209999,
    -    "eth": 607.56,
    -    "eur": 0.845101,
    -    "fjd": 2.052503,
    -    "fkp": 0.744398,
    -    "gbp": 0.74283,
    -    "gel": 2.436399,
    -    "ggp": 0.74271,
    -    "ghs": 4.708496,
    -    "gip": 0.744601,
    -    "gmd": 46.849998,
    -    "gnf": 9005.00012,
    -    "gtq": 7.336024,
    -    "gyd": 207.479996,
    -    "hkd": 7.84544,
    -    "hnl": 23.865999,
    -    "hrk": 6.239802,
    -    "htg": 63.009998,
    -    "huf": 267.98999,
    -    "idr": 13863,
    -    "ils": 3.570702,
    -    "imp": 0.74271,
    -    "inr": 67.037498,
    -    "iqd": 1184,
    -    "irr": 42189.999401,
    -    "isk": 105.250285,
    -    "jep": 0.74271,
    -    "jmd": 127.459999,
    -    "jod": 0.709596,
    -    "jpy": 109.914001,
    -    "kes": 100.599998,
    -    "kgs": 68.425003,
    -    "khr": 4070.000031,
    -    "kmf": 416.950012,
    -    "kpw": 900.000395,
    -    "krw": 1067.319946,
    -    "kwd": 0.301798,
    -    "kyd": 0.819835,
    -    "kzt": 333.399994,
    -    "lak": 8340.000397,
    -    "lbp": 1504.999817,
    -    "lkr": 158.699997,
    -    "lrd": 138.270004,
    -    "lsl": 12.710279,
    -    "ltl": 3.0487,
    -    "lvl": 0.62055,
    -    "lyd": 1.358105,
    -    "mad": 9.413802,
    -    "mdl": 16.724953,
    -    "mga": 3270.000207,
    -    "mkd": 51.770006,
    -    "mmk": 1352.000157,
    -    "mnt": 2406.000152,
    -    "mop": 8.080397,
    -    "mro": 354.000075,
    -    "mur": 33.849998,
    -    "mvr": 15.569619,
    -    "mwk": 717.359985,
    -    "mxn": 20.368401,
    -    "myr": 3.974993,
    -    "mzn": 58.820231,
    -    "nad": 12.716026,
    -    "ngn": 357.999747,
    -    "nio": 31.447599,
    -    "nok": 8.02761,
    -    "npr": 107.099998,
    -    "nzd": 1.418004,
    -    "omr": 0.384801,
    -    "pab": 1,
    -    "pen": 3.260601,
    -    "pgk": 3.260104,
    -    "php": 52.450001,
    -    "pkr": 115.599998,
    -    "pln": 3.603797,
    -    "pyg": 5668.399902,
    -    "qar": 3.639797,
    -    "ron": 3.9354,
    -    "rsd": 99.820999,
    -    "rub": 61.893799,
    -    "rwf": 848.200012,
    -    "sar": 3.749802,
    -    "sbd": 7.933964,
    -    "scr": 13.429735,
    -    "sdg": 17.955099,
    -    "sek": 8.65494,
    -    "sgd": 1.33102,
    -    "shp": 0.744596,
    -    "sll": 7849.99989,
    -    "sos": 562.999736,
    -    "srd": 7.419726,
    -    "std": 20718.800781,
    -    "svc": 8.750051,
    -    "syp": 514.97998,
    -    "szl": 12.716961,
    -    "thb": 31.909815,
    -    "tjs": 9.049018,
    -    "tmt": 3.41,
    -    "tnd": 2.5476,
    -    "top": 2.2843,
    -    "try": 4.562502,
    -    "ttd": 6.649497,
    -    "twd": 29.684036,
    -    "tzs": 2268.000038,
    -    "uah": 26.163009,
    -    "ugx": 3801.999988,
    -    "usd": 1,
    -    "uyu": 31.079742,
    -    "uzs": 7924.999809,
    -    "vef": 79800.000475,
    -    "vnd": 22800,
    -    "vuv": 106.209999,
    -    "wst": 2.562898,
    -    "xaf": 554.150024,
    -    "xag": 0.059753,
    -    "xau": 0.00077,
    -    "xcd": 2.698788,
    -    "xdr": 0.704103,
    -    "xof": 554.150024,
    -    "xpf": 100.903624,
    -    "xrp": 0.6805,
    -    "yer": 249.929993,
    -    "zar": 12.723501,
    -    "zmk": 9001.212517,
    -    "zmw": 10.080297,
    -    "zwl": 322.355011,
    -}
    -
    -
    -CURRNECIES_IDS = dict(zip(range(1, len(CURRENCIES)+1), CURRENCIES.keys()))
    -IDS_CURRENCIES = dict(zip(CURRENCIES.keys(), range(1, len(CURRENCIES)+1)))
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/clients/currencylayer/currencylayer.html b/docs/api/jumpscale/clients/currencylayer/currencylayer.html deleted file mode 100644 index 763fada04..000000000 --- a/docs/api/jumpscale/clients/currencylayer/currencylayer.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - -jumpscale.clients.currencylayer.currencylayer API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.currencylayer.currencylayer

    -
    -
    -
    -Source code -
    from pprint import pprint as print
    -import cryptocompare as cc
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -from jumpscale.loader import j
    -from .currencies import CURRENCIES, CURRNECIES_IDS
    -from pprint import pprint
    -
    -
    -def get_currency_data(api_key, fake=False, fakeonerror=False):
    -    if fake:
    -        return CURRENCIES
    -    else:
    -        url = "http://apilayer.net/api/live?access_key={}".format(api_key)
    -        r = j.tools.http.get(url)
    -        try:
    -            json_res = r.json()
    -            data = json_res["quotes"]
    -
    -            data["USDETH"] = 1 / cc.get_price("ETH", "USD")["ETH"]["USD"]
    -            data["USDXRP"] = cc.get_price("USD", "XRP")["USD"]["XRP"]
    -            data["USDBTC"] = 1 / cc.get_price("BTC", "USD")["BTC"]["USD"]
    -
    -            normalized_data = {k.lower().lstrip("usd"):v for k,v in data.items()}
    -            return normalized_data
    -
    -        except Exception as e:
    -            print("error happened")
    -            if not fakeonerror:
    -                raise e
    -            else:
    -                return CURRENCIES
    -
    -class CurrencyLayerClient(Client):
    -
    -    name = fields.String()
    -    api_key = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__client = None
    -
    -        self._data_cur = {}
    -        self._id2cur = {}
    -        self._cur2id = {}
    -        self.fake = True
    -
    -    def load(self):
    -        # data = self._cache.get("currency_data", get, expire=3600 * 24)
    -        data = get_currency_data(self.api_key, fake=False)
    -        self._data_cur = data
    -
    -    @property
    -    def cur2usd(self):
    -        """
    -        e.g. AED = 3,672 means 3,6... times AED=1 USD
    -        """
    -        if not self._data_cur:
    -            self.load()
    -        return self._data_cur
    -
    -    def cur2usd_print(self):
    -        print(self.cur2usd)
    -
    -    @property
    -    def id2cur(self):
    -        if not self._id2cur:
    -            self._id2cur = CURRNECIES_IDS
    -        return self._id2cur
    -
    -    @property
    -    def cur2id(self):
    -        if not self._cur2id:
    -            self._cur2id = dict(zip(self.id2cur.values(), self.id2cur.keys()))
    -        return self._cur2id
    -
    -    def id2cur_print(self):
    -        pprint(self.id2cur)
    -
    -    def cur2id_print(self):
    -        pprint(self.cur2id)
    -
    -    def test(self):
    -        self._log_info(self.cur2usd)
    -        assert "aed" in self.cur2usd
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def get_currency_data(api_key, fake=False, fakeonerror=False) -
    -
    -
    -
    -Source code -
    def get_currency_data(api_key, fake=False, fakeonerror=False):
    -    if fake:
    -        return CURRENCIES
    -    else:
    -        url = "http://apilayer.net/api/live?access_key={}".format(api_key)
    -        r = j.tools.http.get(url)
    -        try:
    -            json_res = r.json()
    -            data = json_res["quotes"]
    -
    -            data["USDETH"] = 1 / cc.get_price("ETH", "USD")["ETH"]["USD"]
    -            data["USDXRP"] = cc.get_price("USD", "XRP")["USD"]["XRP"]
    -            data["USDBTC"] = 1 / cc.get_price("BTC", "USD")["BTC"]["USD"]
    -
    -            normalized_data = {k.lower().lstrip("usd"):v for k,v in data.items()}
    -            return normalized_data
    -
    -        except Exception as e:
    -            print("error happened")
    -            if not fakeonerror:
    -                raise e
    -            else:
    -                return CURRENCIES
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class CurrencyLayerClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class CurrencyLayerClient(Client):
    -
    -    name = fields.String()
    -    api_key = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__client = None
    -
    -        self._data_cur = {}
    -        self._id2cur = {}
    -        self._cur2id = {}
    -        self.fake = True
    -
    -    def load(self):
    -        # data = self._cache.get("currency_data", get, expire=3600 * 24)
    -        data = get_currency_data(self.api_key, fake=False)
    -        self._data_cur = data
    -
    -    @property
    -    def cur2usd(self):
    -        """
    -        e.g. AED = 3,672 means 3,6... times AED=1 USD
    -        """
    -        if not self._data_cur:
    -            self.load()
    -        return self._data_cur
    -
    -    def cur2usd_print(self):
    -        print(self.cur2usd)
    -
    -    @property
    -    def id2cur(self):
    -        if not self._id2cur:
    -            self._id2cur = CURRNECIES_IDS
    -        return self._id2cur
    -
    -    @property
    -    def cur2id(self):
    -        if not self._cur2id:
    -            self._cur2id = dict(zip(self.id2cur.values(), self.id2cur.keys()))
    -        return self._cur2id
    -
    -    def id2cur_print(self):
    -        pprint(self.id2cur)
    -
    -    def cur2id_print(self):
    -        pprint(self.cur2id)
    -
    -    def test(self):
    -        self._log_info(self.cur2usd)
    -        assert "aed" in self.cur2usd
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var api_key
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var cur2id
    -
    -
    -
    -Source code -
    @property
    -def cur2id(self):
    -    if not self._cur2id:
    -        self._cur2id = dict(zip(self.id2cur.values(), self.id2cur.keys()))
    -    return self._cur2id
    -
    -
    -
    var cur2usd
    -
    -

    e.g. AED = 3,672 means 3,6… times AED=1 USD

    -
    -Source code -
    @property
    -def cur2usd(self):
    -    """
    -    e.g. AED = 3,672 means 3,6... times AED=1 USD
    -    """
    -    if not self._data_cur:
    -        self.load()
    -    return self._data_cur
    -
    -
    -
    var id2cur
    -
    -
    -
    -Source code -
    @property
    -def id2cur(self):
    -    if not self._id2cur:
    -        self._id2cur = CURRNECIES_IDS
    -    return self._id2cur
    -
    -
    -
    var name
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def cur2id_print(self) -
    -
    -
    -
    -Source code -
    def cur2id_print(self):
    -    pprint(self.cur2id)
    -
    -
    -
    -def cur2usd_print(self) -
    -
    -
    -
    -Source code -
    def cur2usd_print(self):
    -    print(self.cur2usd)
    -
    -
    -
    -def id2cur_print(self) -
    -
    -
    -
    -Source code -
    def id2cur_print(self):
    -    pprint(self.id2cur)
    -
    -
    -
    -def load(self) -
    -
    -
    -
    -Source code -
    def load(self):
    -    # data = self._cache.get("currency_data", get, expire=3600 * 24)
    -    data = get_currency_data(self.api_key, fake=False)
    -    self._data_cur = data
    -
    -
    -
    -def test(self) -
    -
    -
    -
    -Source code -
    def test(self):
    -    self._log_info(self.cur2usd)
    -    assert "aed" in self.cur2usd
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/currencylayer/index.html b/docs/api/jumpscale/clients/currencylayer/index.html deleted file mode 100644 index 4cd249156..000000000 --- a/docs/api/jumpscale/clients/currencylayer/index.html +++ /dev/null @@ -1,1519 +0,0 @@ - - - - - - -jumpscale.clients.currencylayer API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.currencylayer

    -
    -
    -
    -
    JS-NG> fake = j.clients.currencylayer.new('fake')
    -
    JS-NG> fake.cur2id_print()
    -
    {'aed': 1,
    -
    'afn': 2,
    -
    'all': 3,
    -
    'amd': 4,
    -
    'ang': 5,
    -
    'aoa': 6,
    -
    'ars': 7,
    -
    'aud': 8,
    -
    'awg': 9,
    -
    'azn': 10,
    -
    'bam': 11,
    -
    'bbd': 12,
    -
    'bdt': 13,
    -
    'bgn': 14,
    -
    'bhd': 15,
    -
    'bif': 16,
    -
    'bmd': 17,
    -
    'bnd': 18,
    -
    'bob': 19,
    -
    'brl': 20,
    -
    'bsd': 21,
    -
    'btc': 22,
    -
    'btn': 23,
    -
    'bwp': 24,
    -
    'byn': 25,
    -
    'byr': 26,
    -
    'bzd': 27,
    -
    'cad': 28,
    -
    'cdf': 29,
    -
    'chf': 30,
    -
    'clf': 31,
    -
    'clp': 32,
    -
    'cny': 33,
    -
    'cop': 34,
    -
    'crc': 35,
    -
    'cuc': 36,
    -
    'cup': 37,
    -
    'cve': 38,
    -
    'czk': 39,
    -
    'djf': 40,
    -
    'dkk': 41,
    -
    'dop': 42,
    -
    'dzd': 43,
    -
    'egp': 44,
    -
    'ern': 45,
    -
    'etb': 46,
    -
    'eth': 47,
    -
    'eur': 48,
    -
    'fjd': 49,
    -
    'fkp': 50,
    -
    'gbp': 51,
    -
    'gel': 52,
    -
    'ggp': 53,
    -
    'ghs': 54,
    -
    'gip': 55,
    -
    'gmd': 56,
    -
    'gnf': 57,
    -
    'gtq': 58,
    -
    'gyd': 59,
    -
    'hkd': 60,
    -
    'hnl': 61,
    -
    'hrk': 62,
    -
    'htg': 63,
    -
    'huf': 64,
    -
    'idr': 65,
    -
    'ils': 66,
    -
    'imp': 67,
    -
    'inr': 68,
    -
    'iqd': 69,
    -
    'irr': 70,
    -
    'isk': 71,
    -
    'jep': 72,
    -
    'jmd': 73,
    -
    'jod': 74,
    -
    'jpy': 75,
    -
    'kes': 76,
    -
    'kgs': 77,
    -
    'khr': 78,
    -
    'kmf': 79,
    -
    'kpw': 80,
    -
    'krw': 81,
    -
    'kwd': 82,
    -
    'kyd': 83,
    -
    'kzt': 84,
    -
    'lak': 85,
    -
    'lbp': 86,
    -
    'lkr': 87,
    -
    'lrd': 88,
    -
    'lsl': 89,
    -
    'ltl': 90,
    -
    'lvl': 91,
    -
    'lyd': 92,
    -
    'mad': 93,
    -
    'mdl': 94,
    -
    'mga': 95,
    -
    'mkd': 96,
    -
    'mmk': 97,
    -
    'mnt': 98,
    -
    'mop': 99,
    -
    'mro': 100,
    -
    'mur': 101,
    -
    'mvr': 102,
    -
    'mwk': 103,
    -
    'mxn': 104,
    -
    'myr': 105,
    -
    'mzn': 106,
    -
    'nad': 107,
    -
    'ngn': 108,
    -
    'nio': 109,
    -
    'nok': 110,
    -
    'npr': 111,
    -
    'nzd': 112,
    -
    'omr': 113,
    -
    'pab': 114,
    -
    'pen': 115,
    -
    'pgk': 116,
    -
    'php': 117,
    -
    'pkr': 118,
    -
    'pln': 119,
    -
    'pyg': 120,
    -
    'qar': 121,
    -
    'ron': 122,
    -
    'rsd': 123,
    -
    'rub': 124,
    -
    'rwf': 125,
    -
    'sar': 126,
    -
    'sbd': 127,
    -
    'scr': 128,
    -
    'sdg': 129,
    -
    'sek': 130,
    -
    'sgd': 131,
    -
    'shp': 132,
    -
    'sll': 133,
    -
    'sos': 134,
    -
    'srd': 135,
    -
    'std': 136,
    -
    'svc': 137,
    -
    'syp': 138,
    -
    'szl': 139,
    -
    'thb': 140,
    -
    'tjs': 141,
    -
    'tmt': 142,
    -
    'tnd': 143,
    -
    'top': 144,
    -
    'try': 145,
    -
    'ttd': 146,
    -
    'twd': 147,
    -
    'tzs': 148,
    -
    'uah': 149,
    -
    'ugx': 150,
    -
    'usd': 151,
    -
    'uyu': 152,
    -
    'uzs': 153,
    -
    'vef': 154,
    -
    'vnd': 155,
    -
    'vuv': 156,
    -
    'wst': 157,
    -
    'xaf': 158,
    -
    'xag': 159,
    -
    'xau': 160,
    -
    'xcd': 161,
    -
    'xdr': 162,
    -
    'xof': 163,
    -
    'xpf': 164,
    -
    'xrp': 165,
    -
    'yer': 166,
    -
    'zar': 167,
    -
    'zmk': 168,
    -
    'zmw': 169,
    -
    'zwl': 170}
    -
    JS-NG> fake.id2cur_print()
    -
    {1: 'aed',
    -
    2: 'afn',
    -
    3: 'all',
    -
    4: 'amd',
    -
    5: 'ang',
    -
    6: 'aoa',
    -
    7: 'ars',
    -
    8: 'aud',
    -
    9: 'awg',
    -
    10: 'azn',
    -
    11: 'bam',
    -
    12: 'bbd',
    -
    13: 'bdt',
    -
    14: 'bgn',
    -
    15: 'bhd',
    -
    16: 'bif',
    -
    17: 'bmd',
    -
    18: 'bnd',
    -
    19: 'bob',
    -
    20: 'brl',
    -
    21: 'bsd',
    -
    22: 'btc',
    -
    23: 'btn',
    -
    24: 'bwp',
    -
    25: 'byn',
    -
    26: 'byr',
    -
    27: 'bzd',
    -
    28: 'cad',
    -
    29: 'cdf',
    -
    30: 'chf',
    -
    31: 'clf',
    -
    32: 'clp',
    -
    33: 'cny',
    -
    34: 'cop',
    -
    35: 'crc',
    -
    36: 'cuc',
    -
    37: 'cup',
    -
    38: 'cve',
    -
    39: 'czk',
    -
    40: 'djf',
    -
    41: 'dkk',
    -
    42: 'dop',
    -
    43: 'dzd',
    -
    44: 'egp',
    -
    45: 'ern',
    -
    46: 'etb',
    -
    47: 'eth',
    -
    48: 'eur',
    -
    49: 'fjd',
    -
    50: 'fkp',
    -
    51: 'gbp',
    -
    52: 'gel',
    -
    53: 'ggp',
    -
    54: 'ghs',
    -
    55: 'gip',
    -
    56: 'gmd',
    -
    57: 'gnf',
    -
    58: 'gtq',
    -
    59: 'gyd',
    -
    60: 'hkd',
    -
    61: 'hnl',
    -
    62: 'hrk',
    -
    63: 'htg',
    -
    64: 'huf',
    -
    65: 'idr',
    -
    66: 'ils',
    -
    67: 'imp',
    -
    68: 'inr',
    -
    69: 'iqd',
    -
    70: 'irr',
    -
    71: 'isk',
    -
    72: 'jep',
    -
    73: 'jmd',
    -
    74: 'jod',
    -
    75: 'jpy',
    -
    76: 'kes',
    -
    77: 'kgs',
    -
    78: 'khr',
    -
    79: 'kmf',
    -
    80: 'kpw',
    -
    81: 'krw',
    -
    82: 'kwd',
    -
    83: 'kyd',
    -
    84: 'kzt',
    -
    85: 'lak',
    -
    86: 'lbp',
    -
    87: 'lkr',
    -
    88: 'lrd',
    -
    89: 'lsl',
    -
    90: 'ltl',
    -
    91: 'lvl',
    -
    92: 'lyd',
    -
    93: 'mad',
    -
    94: 'mdl',
    -
    95: 'mga',
    -
    96: 'mkd',
    -
    97: 'mmk',
    -
    98: 'mnt',
    -
    99: 'mop',
    -
    100: 'mro',
    -
    101: 'mur',
    -
    102: 'mvr',
    -
    103: 'mwk',
    -
    104: 'mxn',
    -
    105: 'myr',
    -
    106: 'mzn',
    -
    107: 'nad',
    -
    108: 'ngn',
    -
    109: 'nio',
    -
    110: 'nok',
    -
    111: 'npr',
    -
    112: 'nzd',
    -
    113: 'omr',
    -
    114: 'pab',
    -
    115: 'pen',
    -
    116: 'pgk',
    -
    117: 'php',
    -
    118: 'pkr',
    -
    119: 'pln',
    -
    120: 'pyg',
    -
    121: 'qar',
    -
    122: 'ron',
    -
    123: 'rsd',
    -
    124: 'rub',
    -
    125: 'rwf',
    -
    126: 'sar',
    -
    127: 'sbd',
    -
    128: 'scr',
    -
    129: 'sdg',
    -
    130: 'sek',
    -
    131: 'sgd',
    -
    132: 'shp',
    -
    133: 'sll',
    -
    134: 'sos',
    -
    135: 'srd',
    -
    136: 'std',
    -
    137: 'svc',
    -
    138: 'syp',
    -
    139: 'szl',
    -
    140: 'thb',
    -
    141: 'tjs',
    -
    142: 'tmt',
    -
    143: 'tnd',
    -
    144: 'top',
    -
    145: 'try',
    -
    146: 'ttd',
    -
    147: 'twd',
    -
    148: 'tzs',
    -
    149: 'uah',
    -
    150: 'ugx',
    -
    151: 'usd',
    -
    152: 'uyu',
    -
    153: 'uzs',
    -
    154: 'vef',
    -
    155: 'vnd',
    -
    156: 'vuv',
    -
    157: 'wst',
    -
    158: 'xaf',
    -
    159: 'xag',
    -
    160: 'xau',
    -
    161: 'xcd',
    -
    162: 'xdr',
    -
    163: 'xof',
    -
    164: 'xpf',
    -
    165: 'xrp',
    -
    166: 'yer',
    -
    167: 'zar',
    -
    168: 'zmk',
    -
    169: 'zmw',
    -
    170: 'zwl'}
    -
    JS-NG> fake.id2cur
    -
    {1: 'aed', 2: 'afn', 3: 'all', 4: 'amd', 5: 'ang', 6: 'aoa', 7: 'ars', 8: 'aud', 9: 'awg', 10: 'azn', 11: 'bam', 12: 'b
    -
    bd', 13: 'bdt', 14: 'bgn', 15: 'bhd', 16: 'bif', 17: 'bmd', 18: 'bnd', 19: 'bob', 20: 'brl', 21: 'bsd', 22: 'btc', 23:
    -
    'btn', 24: 'bwp', 25: 'byn', 26: 'byr', 27: 'bzd', 28: 'cad', 29: 'cdf', 30: 'chf', 31: 'clf', 32: 'clp', 33: 'cny', 34
    -
    'cop', 35: 'crc', 36: 'cuc', 37: 'cup', 38: 'cve', 39: 'czk', 40: 'djf', 41: 'dkk', 42: 'dop', 43: 'dzd', 44: 'egp', -45: 'ern', 46: 'etb', 47: 'eth', 48: 'eur', 49: 'fjd', 50: 'fkp', 51: 'gbp', 52: 'gel', 53: 'ggp', 54: 'ghs', 55: 'gip' -, 56: 'gmd', 57: 'gnf', 58: 'gtq', 59: 'gyd', 60: 'hkd', 61: 'hnl', 62: 'hrk', 63: 'htg', 64: 'huf', 65: 'idr', 66: 'ils', 67: 'imp', 68: 'inr', 69: 'iqd', 70: 'irr', 71: 'isk', 72: 'jep', 73: 'jmd', 74: 'jod', 75: 'jpy', 76: 'kes', 77: 'kgs', 78: 'khr', 79: 'kmf', 80: 'kpw', 81: 'krw', 82: 'kwd', 83: 'kyd', 84: 'kzt', 85: 'lak', 86: 'lbp', 87: 'lkr', 88: 'lrd', 89: 'lsl', 90: 'ltl', 91: 'lvl', 92: 'lyd', 93: 'mad', 94: 'mdl', 95: 'mga', 96: 'mkd', 97: 'mmk', 98: 'mnt', 99: 'mop', 100: 'mro', 101: 'mur', 102: 'mvr', 103: 'mwk', 104: 'mxn', 105: 'myr', 106: 'mzn', 107: 'nad', 108: 'ngn', 109: 'nio', 110: 'nok', 111: 'npr', 112: 'nzd', 113: 'omr', 114: 'pab', 115: 'pen', 116: 'pgk', 117: 'php', 118: 'pkr', 119: 'pln', 120: 'pyg', 121: 'qar', 122: 'ron', 123: 'rsd', 124: 'rub', 125: 'rwf', 126: 'sar', 127: 'sbd', 128: 'scr', 129: 'sdg', 130: 'sek', 131: 'sgd', 132: 'shp', 133: 'sll', 134: 'sos', 135: 'srd', 136: 'std', 137: 'svc', 138: 'syp', 139: 'szl', 140: 'thb', 141: 'tjs', 142: 'tmt', 143: 'tnd', 144: 'top', 145: 'try', 146: 'ttd', 147: 'twd', 148: 'tzs', 149: 'uah', 150: 'ugx', 151: 'usd', 152: 'uyu', 153: 'uzs', 154: 'vef', 155: 'vnd', 156: 'vuv', 157: 'wst', 158: 'xaf', 159: 'xag', 160: 'xau', 161: 'xcd', 162: 'xdr', 163: 'xof', 164: 'xpf', 165: 'xrp', 166: 'yer', 167: 'zar', 168: 'zmk', 169: 'zmw', 170: 'zwl'}
    -
    -

    JS-NG> fake.cur2id -
    -{'aed': 1, 'afn': 2, 'all': 3, 'amd': 4, 'ang': 5, 'aoa': 6, 'ars': 7, 'aud': 8, 'awg': 9, 'azn': 10, 'bam': 11, 'bbd': -12, 'bdt': 13, 'bgn': 14, 'bhd': 15, 'bif': 16, 'bmd': 17, 'bnd': 18, 'bob': 19, 'brl': 20, 'bsd': 21, 'btc': 22, 'btn -': 23, 'bwp': 24, 'byn': 25, 'byr': 26, 'bzd': 27, 'cad': 28, 'cdf': 29, 'chf': 30, 'clf': 31, 'clp': 32, 'cny': 33, 'c -op': 34, 'crc': 35, 'cuc': 36, 'cup': 37, 'cve': 38, 'czk': 39, 'djf': 40, 'dkk': 41, 'dop': 42, 'dzd': 43, 'egp': 44, -'ern': 45, 'etb': 46, 'eth': 47, 'eur': 48, 'fjd': 49, 'fkp': 50, 'gbp': 51, 'gel': 52, 'ggp': 53, 'ghs': 54, 'gip': 55 -, 'gmd': 56, 'gnf': 57, 'gtq': 58, 'gyd': 59, 'hkd': 60, 'hnl': 61, 'hrk': 62, 'htg': 63, 'huf': 64, 'idr': 65, 'ils': 66, 'imp': 67, 'inr': 68, 'iqd': 69, 'irr': 70, 'isk': 71, 'jep': 72, 'jmd': 73, 'jod': 74, 'jpy': 75, 'kes': 76, 'kgs': 77, 'khr': 78, 'kmf': 79, 'kpw': 80, 'krw': 81, 'kwd': 82, 'kyd': 83, 'kzt': 84, 'lak': 85, 'lbp': 86, 'lkr': 87, 'lrd': 88, 'lsl': 89, 'ltl': 90, 'lvl': 91, 'lyd': 92, 'mad': 93, 'mdl': 94, 'mga': 95, 'mkd': 96, 'mmk': 97, 'mnt': 98, 'mop': 99, 'mro': 100, 'mur': 101, 'mvr': 102, 'mwk': 103, 'mxn': 104, 'myr': 105, 'mzn': 106, 'nad': 107, 'ngn': 108, 'nio': 109, 'nok': 110, 'npr': 111, 'nzd': 112, 'omr': 113, 'pab': 114, 'pen': 115, 'pgk': 116, 'php': 117, 'pkr': 118, 'pln': 119, 'pyg': 120, 'qar': 121, 'ron': 122, 'rsd': 123, 'rub': 124, 'rwf': 125, 'sar': 126, 'sbd': 127, 'scr': 128, 'sdg': 129, 'sek': 130, 'sgd': 131, 'shp': 132, 'sll': 133, 'sos': 134, 'srd': 135, 'std': 136, 'svc': 137, 'syp': 138, 'szl': 139, 'thb': 140, 'tjs': 141, 'tmt': 142, 'tnd': 143, 'top': 144, 'try': 145, 'ttd': 146, 'twd': 147, 'tzs': 148, 'uah': 149, 'ugx': 150, 'usd': 151, 'uyu': 152, 'uzs': 153, 'vef': 154, 'vnd': 155, 'vuv': 156, 'wst': 157, 'xaf': 158, 'xag': 159, 'xau': 160, 'xcd': 161, 'xdr': 162, 'xof': 163, 'xpf': 164, 'xrp': 165, 'yer': 166, 'zar': 167, 'zmk': 168, 'zmw': 169, 'zwl': 170}

    -

    JS-NG> -
    -JS-NG> fake.api_key="VALID KEY" -
    -JS-NG> j.clients.currencylayer.fake.load() -
    -JS-NG> j.clients.currencylayer.fake.id2cur_print() -
    -{1: 'aed', -2: 'afn', -3: 'all', -4: 'amd', -5: 'ang', -6: 'aoa', -7: 'ars', -8: 'aud', -9: 'awg', -10: 'azn', -11: 'bam', -12: 'bbd', -13: 'bdt', -14: 'bgn', -15: 'bhd', -16: 'bif', -17: 'bmd', -18: 'bnd', -19: 'bob', -20: 'brl', -21: 'bsd', -22: 'btc', -23: 'btn', -24: 'bwp', -25: 'byn', -26: 'byr', -27: 'bzd', -28: 'cad', -29: 'cdf', -30: 'chf', -31: 'clf', -32: 'clp', -33: 'cny', -34: 'cop', -35: 'crc', -36: 'cuc', -37: 'cup', -38: 'cve', -39: 'czk', -40: 'djf', -41: 'dkk', -42: 'dop', -43: 'dzd', -44: 'egp', -45: 'ern', -46: 'etb', -47: 'eth', -48: 'eur', -49: 'fjd', -50: 'fkp', -51: 'gbp', -52: 'gel', -53: 'ggp', -54: 'ghs', -55: 'gip', -56: 'gmd', -57: 'gnf', -58: 'gtq', -59: 'gyd', -60: 'hkd', -61: 'hnl', -62: 'hrk', -63: 'htg', -64: 'huf', -65: 'idr', -66: 'ils', -67: 'imp', -68: 'inr', -69: 'iqd', -70: 'irr', -71: 'isk', -72: 'jep', -73: 'jmd', -74: 'jod', -75: 'jpy', -76: 'kes', -77: 'kgs', -78: 'khr', -79: 'kmf', -80: 'kpw', -81: 'krw', -82: 'kwd', -83: 'kyd', -84: 'kzt', -85: 'lak', -86: 'lbp', -87: 'lkr', -88: 'lrd', -89: 'lsl', -90: 'ltl', -91: 'lvl', -92: 'lyd', -93: 'mad', -94: 'mdl', -95: 'mga', -96: 'mkd', -97: 'mmk', -98: 'mnt', -99: 'mop', -100: 'mro', -101: 'mur', -102: 'mvr', -103: 'mwk', -104: 'mxn', -105: 'myr', -106: 'mzn', -107: 'nad', -108: 'ngn', -109: 'nio', -110: 'nok', -111: 'npr', -112: 'nzd', -113: 'omr', -114: 'pab', -115: 'pen', -116: 'pgk', -117: 'php', -118: 'pkr', -119: 'pln', -120: 'pyg', -121: 'qar', -122: 'ron', -123: 'rsd', -124: 'rub', -125: 'rwf', -126: 'sar', -127: 'sbd', -128: 'scr', -129: 'sdg', -130: 'sek', -131: 'sgd', -132: 'shp', -133: 'sll', -134: 'sos', -135: 'srd', -136: 'std', -137: 'svc', -138: 'syp', -139: 'szl', -140: 'thb', -141: 'tjs', -142: 'tmt', -143: 'tnd', -144: 'top', -145: 'try', -146: 'ttd', -147: 'twd', -148: 'tzs', -149: 'uah', -150: 'ugx', -151: 'usd', -152: 'uyu', -153: 'uzs', -154: 'vef', -155: 'vnd', -156: 'vuv', -157: 'wst', -158: 'xaf', -159: 'xag', -160: 'xau', -161: 'xcd', -162: 'xdr', -163: 'xof', -164: 'xpf', -165: 'xrp', -166: 'yer', -167: 'zar', -168: 'zmk', -169: 'zmw', -170: 'zwl'} -JS-NG> j.clients.currencylayer.fake.cur2usd_print() -
    -{'': 1, -'aed': 3.672979, -'afn': 78.296617, -'ah': 24.914996, -'all': 109.150047, -'amd': 476.210221, -'ang': 1.78525, -'aoa': 362.0025, -'ar': 3.75045, -'ars': 55.394992, -'aud': 1.474703, -'awg': 1.8, -'azn': 1.704964, -'bam': 1.758993, -'bbd': 2.0194, -'bd': 8.221403, -'bdt': 83.745499, -'bgn': 1.760801, -'bhd': 0.375961, -'bif': 1855, -'bmd': 1, -'bnd': 1.350696, -'bob': 6.86065, -'brl': 4.152695, -'bsd': 0.99205, -'btc': 9.948852946999476e-05, -'btn': 71.884502, -'bwp': 10.961999, -'byn': 2.060501, -'byr': 19600, -'bzd': 2.01595, -'cad': 1.32733, -'cdf': 1659.99946, -'chf': 0.978545, -'clf': 0.026094, -'clp': 720.00501, -'cny': 7.151304, -'cop': 3431.55, -'cr': 13.669974, -'crc': 567.080062, -'cuc': 1, -'cup': 26.5, -'cve': 98.749501, -'czk': 23.208988, -'egp': 16.53602, -'ek': 9.67235, -'ern': 14.999484, -'etb': 29.000284, -'eth': 0.005395489370885939, -'eur': 0.900035, -'fjd': 2.17495, -'fkp': 0.81691, -'g': 45.119039, -'gbp': 0.81752, -'gd': 1.38792, -'gel': 2.925034, -'ggp': 0.81764, -'ghs': 5.402501, -'gip': 0.81691, -'gmd': 50.415037, -'gnf': 9239.999966, -'gtq': 7.680957, -'gx': 3685.496424, -'gyd': 209.244968, -'hkd': 7.84595, -'hnl': 24.674984, -'hp': 1.320898, -'hrk': 6.653399, -'htg': 95.361503, -'huf': 296.280997, -'idr': 14258.25, -'ils': 3.52095, -'imp': 0.81764, -'inr': 71.792403, -'iqd': 1190, -'irr': 42104.999481, -'isk': 124.829491, -'jep': 0.81764, -'jf': 177.720165, -'jmd': 134.559965, -'jod': 0.7084, -'jpy': 106.015996, -'kes': 103.389937, -'kgs': 69.8159, -'khr': 4140.000279, -'kk': 6.71151, -'kmf': 443.249767, -'kpw': 900.052015, -'krw': 1214.824979, -'kwd': 0.303901, -'kyd': 0.83355, -'kzt': 383.110385, -'lak': 8735.000017, -'lbp': 1507.949729, -'lkr': 179.605474, -'ll': 9299.999946, -'lrd': 205.000232, -'lsl': 15.250149, -'ltl': 2.95274, -'lvl': 0.60489, -'lyd': 1.40503, -'mad': 9.5685, -'mdl': 17.887498, -'mga': 3674.999563, -'mkd': 55.324023, -'mmk': 1516.702673, -'mnt': 2669.391245, -'mop': 8.080496, -'mro': 357.000024, -'mur': 36.043506, -'mvr': 15.410297, -'mwk': 731.210149, -'mxn': 19.92145, -'myr': 4.198897, -'mzn': 61.020166, -'nad': 15.270055, -'ngn': 362.000148, -'nio': 33.602406, -'nok': 8.988065, -'npr': 115.010199, -'nzd': 1.56365, -'omr': 0.384976, -'op': 51.294983, -'os': 579.999893, -'pab': 0.99205, -'pen': 3.37635, -'pgk': 3.397801, -'php': 52.438012, -'pkr': 157.249855, -'pln': 3.92254, -'pyg': 6217.103241, -'qar': 3.64175, -'rd': 7.457963, -'ron': 4.256202, -'rsd': 106.069758, -'rub': 66.06102, -'rwf': 910, -'td': 21560.79, -'thb': 30.589849, -'tjs': 9.696302, -'tmt': 3.5, -'tnd': 2.857701, -'top': 2.320597, -'try': 5.81132, -'ttd': 6.71695, -'twd': 31.400972, -'tzs': 2298.149889, -'vc': 8.75195, -'vef': 9.987501, -'vnd': 23199, -'vuv': 117.90362, -'wst': 2.675215, -'xaf': 589.959986, -'xag': 0.056555, -'xau': 0.000653, -'xcd': 2.70245, -'xdr': 0.729108, -'xof': 584.499865, -'xpf': 106.950279, -'xrp': 3.771, -'yer': 250.349819, -'yp': 515.000236, -'yu': 36.34003, -'zar': 15.26498, -'zd': 119.879946, -'zl': 15.269489, -'zmk': 9001.202171, -'zmw': 13.112024, -'zs': 9376.306597, -'zwl': 322.000001}

    -
    -Source code -
    """
    -JS-NG> fake = j.clients.currencylayer.new('fake')
    -JS-NG> fake.cur2id_print()
    -{'aed': 1,
    - 'afn': 2,
    - 'all': 3,
    - 'amd': 4,
    - 'ang': 5,
    - 'aoa': 6,
    - 'ars': 7,
    - 'aud': 8,
    - 'awg': 9,
    - 'azn': 10,
    - 'bam': 11,
    - 'bbd': 12,
    - 'bdt': 13,
    - 'bgn': 14,
    - 'bhd': 15,
    - 'bif': 16,
    - 'bmd': 17,
    - 'bnd': 18,
    - 'bob': 19,
    - 'brl': 20,
    - 'bsd': 21,
    - 'btc': 22,
    - 'btn': 23,
    - 'bwp': 24,
    - 'byn': 25,
    - 'byr': 26,
    - 'bzd': 27,
    - 'cad': 28,
    - 'cdf': 29,
    - 'chf': 30,
    - 'clf': 31,
    - 'clp': 32,
    - 'cny': 33,
    - 'cop': 34,
    - 'crc': 35,
    - 'cuc': 36,
    - 'cup': 37,
    - 'cve': 38,
    - 'czk': 39,
    - 'djf': 40,
    - 'dkk': 41,
    - 'dop': 42,
    - 'dzd': 43,
    - 'egp': 44,
    - 'ern': 45,
    - 'etb': 46,
    - 'eth': 47,
    - 'eur': 48,
    - 'fjd': 49,
    - 'fkp': 50,
    - 'gbp': 51,
    - 'gel': 52,
    - 'ggp': 53,
    - 'ghs': 54,
    - 'gip': 55,
    - 'gmd': 56,
    - 'gnf': 57,
    - 'gtq': 58,
    - 'gyd': 59,
    - 'hkd': 60,
    - 'hnl': 61,
    - 'hrk': 62,
    - 'htg': 63,
    - 'huf': 64,
    - 'idr': 65,
    - 'ils': 66,
    - 'imp': 67,
    - 'inr': 68,
    - 'iqd': 69,
    - 'irr': 70,
    - 'isk': 71,
    - 'jep': 72,
    - 'jmd': 73,
    - 'jod': 74,
    - 'jpy': 75,
    - 'kes': 76,
    - 'kgs': 77,
    - 'khr': 78,
    - 'kmf': 79,
    - 'kpw': 80,
    - 'krw': 81,
    - 'kwd': 82,
    - 'kyd': 83,
    - 'kzt': 84,
    - 'lak': 85,
    - 'lbp': 86,
    - 'lkr': 87,
    - 'lrd': 88,
    - 'lsl': 89,
    - 'ltl': 90,
    - 'lvl': 91,
    - 'lyd': 92,
    - 'mad': 93,
    - 'mdl': 94,
    - 'mga': 95,
    - 'mkd': 96,
    - 'mmk': 97,
    - 'mnt': 98,
    - 'mop': 99,
    - 'mro': 100,
    - 'mur': 101,
    - 'mvr': 102,
    - 'mwk': 103,
    - 'mxn': 104,
    - 'myr': 105,
    - 'mzn': 106,
    - 'nad': 107,
    - 'ngn': 108,
    - 'nio': 109,
    - 'nok': 110,
    - 'npr': 111,
    - 'nzd': 112,
    - 'omr': 113,
    - 'pab': 114,
    - 'pen': 115,
    - 'pgk': 116,
    - 'php': 117,
    - 'pkr': 118,
    - 'pln': 119,
    - 'pyg': 120,
    - 'qar': 121,
    - 'ron': 122,
    - 'rsd': 123,
    - 'rub': 124,
    - 'rwf': 125,
    - 'sar': 126,
    - 'sbd': 127,
    - 'scr': 128,
    - 'sdg': 129,
    - 'sek': 130,
    - 'sgd': 131,
    - 'shp': 132,
    - 'sll': 133,
    - 'sos': 134,
    - 'srd': 135,
    - 'std': 136,
    - 'svc': 137,
    - 'syp': 138,
    - 'szl': 139,
    - 'thb': 140,
    - 'tjs': 141,
    - 'tmt': 142,
    - 'tnd': 143,
    - 'top': 144,
    - 'try': 145,
    - 'ttd': 146,
    - 'twd': 147,
    - 'tzs': 148,
    - 'uah': 149,
    - 'ugx': 150,
    - 'usd': 151,
    - 'uyu': 152,
    - 'uzs': 153,
    - 'vef': 154,
    - 'vnd': 155,
    - 'vuv': 156,
    - 'wst': 157,
    - 'xaf': 158,
    - 'xag': 159,
    - 'xau': 160,
    - 'xcd': 161,
    - 'xdr': 162,
    - 'xof': 163,
    - 'xpf': 164,
    - 'xrp': 165,
    - 'yer': 166,
    - 'zar': 167,
    - 'zmk': 168,
    - 'zmw': 169,
    - 'zwl': 170}
    -JS-NG> fake.id2cur_print()
    -{1: 'aed',
    - 2: 'afn',
    - 3: 'all',
    - 4: 'amd',
    - 5: 'ang',
    - 6: 'aoa',
    - 7: 'ars',
    - 8: 'aud',
    - 9: 'awg',
    - 10: 'azn',
    - 11: 'bam',
    - 12: 'bbd',
    - 13: 'bdt',
    - 14: 'bgn',
    - 15: 'bhd',
    - 16: 'bif',
    - 17: 'bmd',
    - 18: 'bnd',
    - 19: 'bob',
    - 20: 'brl',
    - 21: 'bsd',
    - 22: 'btc',
    - 23: 'btn',
    - 24: 'bwp',
    - 25: 'byn',
    - 26: 'byr',
    - 27: 'bzd',
    - 28: 'cad',
    - 29: 'cdf',
    - 30: 'chf',
    - 31: 'clf',
    - 32: 'clp',
    - 33: 'cny',
    - 34: 'cop',
    - 35: 'crc',
    - 36: 'cuc',
    - 37: 'cup',
    - 38: 'cve',
    - 39: 'czk',
    - 40: 'djf',
    - 41: 'dkk',
    - 42: 'dop',
    - 43: 'dzd',
    - 44: 'egp',
    - 45: 'ern',
    - 46: 'etb',
    - 47: 'eth',
    - 48: 'eur',
    - 49: 'fjd',
    - 50: 'fkp',
    - 51: 'gbp',
    - 52: 'gel',
    - 53: 'ggp',
    - 54: 'ghs',
    - 55: 'gip',
    - 56: 'gmd',
    - 57: 'gnf',
    - 58: 'gtq',
    - 59: 'gyd',
    - 60: 'hkd',
    - 61: 'hnl',
    - 62: 'hrk',
    - 63: 'htg',
    - 64: 'huf',
    - 65: 'idr',
    - 66: 'ils',
    - 67: 'imp',
    - 68: 'inr',
    - 69: 'iqd',
    - 70: 'irr',
    - 71: 'isk',
    - 72: 'jep',
    - 73: 'jmd',
    - 74: 'jod',
    - 75: 'jpy',
    - 76: 'kes',
    - 77: 'kgs',
    - 78: 'khr',
    - 79: 'kmf',
    - 80: 'kpw',
    - 81: 'krw',
    - 82: 'kwd',
    - 83: 'kyd',
    - 84: 'kzt',
    - 85: 'lak',
    - 86: 'lbp',
    - 87: 'lkr',
    - 88: 'lrd',
    - 89: 'lsl',
    - 90: 'ltl',
    - 91: 'lvl',
    - 92: 'lyd',
    - 93: 'mad',
    - 94: 'mdl',
    - 95: 'mga',
    - 96: 'mkd',
    - 97: 'mmk',
    - 98: 'mnt',
    - 99: 'mop',
    - 100: 'mro',
    - 101: 'mur',
    - 102: 'mvr',
    - 103: 'mwk',
    - 104: 'mxn',
    - 105: 'myr',
    - 106: 'mzn',
    - 107: 'nad',
    - 108: 'ngn',
    - 109: 'nio',
    - 110: 'nok',
    - 111: 'npr',
    - 112: 'nzd',
    - 113: 'omr',
    - 114: 'pab',
    - 115: 'pen',
    - 116: 'pgk',
    - 117: 'php',
    - 118: 'pkr',
    - 119: 'pln',
    - 120: 'pyg',
    - 121: 'qar',
    - 122: 'ron',
    - 123: 'rsd',
    - 124: 'rub',
    - 125: 'rwf',
    - 126: 'sar',
    - 127: 'sbd',
    - 128: 'scr',
    - 129: 'sdg',
    - 130: 'sek',
    - 131: 'sgd',
    - 132: 'shp',
    - 133: 'sll',
    - 134: 'sos',
    - 135: 'srd',
    - 136: 'std',
    - 137: 'svc',
    - 138: 'syp',
    - 139: 'szl',
    - 140: 'thb',
    - 141: 'tjs',
    - 142: 'tmt',
    - 143: 'tnd',
    - 144: 'top',
    - 145: 'try',
    - 146: 'ttd',
    - 147: 'twd',
    - 148: 'tzs',
    - 149: 'uah',
    - 150: 'ugx',
    - 151: 'usd',
    - 152: 'uyu',
    - 153: 'uzs',
    - 154: 'vef',
    - 155: 'vnd',
    - 156: 'vuv',
    - 157: 'wst',
    - 158: 'xaf',
    - 159: 'xag',
    - 160: 'xau',
    - 161: 'xcd',
    - 162: 'xdr',
    - 163: 'xof',
    - 164: 'xpf',
    - 165: 'xrp',
    - 166: 'yer',
    - 167: 'zar',
    - 168: 'zmk',
    - 169: 'zmw',
    - 170: 'zwl'}
    -JS-NG> fake.id2cur
    -{1: 'aed', 2: 'afn', 3: 'all', 4: 'amd', 5: 'ang', 6: 'aoa', 7: 'ars', 8: 'aud', 9: 'awg', 10: 'azn', 11: 'bam', 12: 'b
    -bd', 13: 'bdt', 14: 'bgn', 15: 'bhd', 16: 'bif', 17: 'bmd', 18: 'bnd', 19: 'bob', 20: 'brl', 21: 'bsd', 22: 'btc', 23:
    -'btn', 24: 'bwp', 25: 'byn', 26: 'byr', 27: 'bzd', 28: 'cad', 29: 'cdf', 30: 'chf', 31: 'clf', 32: 'clp', 33: 'cny', 34
    -: 'cop', 35: 'crc', 36: 'cuc', 37: 'cup', 38: 'cve', 39: 'czk', 40: 'djf', 41: 'dkk', 42: 'dop', 43: 'dzd', 44: 'egp',
    -45: 'ern', 46: 'etb', 47: 'eth', 48: 'eur', 49: 'fjd', 50: 'fkp', 51: 'gbp', 52: 'gel', 53: 'ggp', 54: 'ghs', 55: 'gip'
    -, 56: 'gmd', 57: 'gnf', 58: 'gtq', 59: 'gyd', 60: 'hkd', 61: 'hnl', 62: 'hrk', 63: 'htg', 64: 'huf', 65: 'idr', 66: 'ils', 67: 'imp', 68: 'inr', 69: 'iqd', 70: 'irr', 71: 'isk', 72: 'jep', 73: 'jmd', 74: 'jod', 75: 'jpy', 76: 'kes', 77: 'kgs', 78: 'khr', 79: 'kmf', 80: 'kpw', 81: 'krw', 82: 'kwd', 83: 'kyd', 84: 'kzt', 85: 'lak', 86: 'lbp', 87: 'lkr', 88: 'lrd', 89: 'lsl', 90: 'ltl', 91: 'lvl', 92: 'lyd', 93: 'mad', 94: 'mdl', 95: 'mga', 96: 'mkd', 97: 'mmk', 98: 'mnt', 99: 'mop', 100: 'mro', 101: 'mur', 102: 'mvr', 103: 'mwk', 104: 'mxn', 105: 'myr', 106: 'mzn', 107: 'nad', 108: 'ngn', 109: 'nio', 110: 'nok', 111: 'npr', 112: 'nzd', 113: 'omr', 114: 'pab', 115: 'pen', 116: 'pgk', 117: 'php', 118: 'pkr', 119: 'pln', 120: 'pyg', 121: 'qar', 122: 'ron', 123: 'rsd', 124: 'rub', 125: 'rwf', 126: 'sar', 127: 'sbd', 128: 'scr', 129: 'sdg', 130: 'sek', 131: 'sgd', 132: 'shp', 133: 'sll', 134: 'sos', 135: 'srd', 136: 'std', 137: 'svc', 138: 'syp', 139: 'szl', 140: 'thb', 141: 'tjs', 142: 'tmt', 143: 'tnd', 144: 'top', 145: 'try', 146: 'ttd', 147: 'twd', 148: 'tzs', 149: 'uah', 150: 'ugx', 151: 'usd', 152: 'uyu', 153: 'uzs', 154: 'vef', 155: 'vnd', 156: 'vuv', 157: 'wst', 158: 'xaf', 159: 'xag', 160: 'xau', 161: 'xcd', 162: 'xdr', 163: 'xof', 164: 'xpf', 165: 'xrp', 166: 'yer', 167: 'zar', 168: 'zmk', 169: 'zmw', 170: 'zwl'}
    -
    -JS-NG> fake.cur2id
    -{'aed': 1, 'afn': 2, 'all': 3, 'amd': 4, 'ang': 5, 'aoa': 6, 'ars': 7, 'aud': 8, 'awg': 9, 'azn': 10, 'bam': 11, 'bbd':
    - 12, 'bdt': 13, 'bgn': 14, 'bhd': 15, 'bif': 16, 'bmd': 17, 'bnd': 18, 'bob': 19, 'brl': 20, 'bsd': 21, 'btc': 22, 'btn
    -': 23, 'bwp': 24, 'byn': 25, 'byr': 26, 'bzd': 27, 'cad': 28, 'cdf': 29, 'chf': 30, 'clf': 31, 'clp': 32, 'cny': 33, 'c
    -op': 34, 'crc': 35, 'cuc': 36, 'cup': 37, 'cve': 38, 'czk': 39, 'djf': 40, 'dkk': 41, 'dop': 42, 'dzd': 43, 'egp': 44,
    -'ern': 45, 'etb': 46, 'eth': 47, 'eur': 48, 'fjd': 49, 'fkp': 50, 'gbp': 51, 'gel': 52, 'ggp': 53, 'ghs': 54, 'gip': 55
    -, 'gmd': 56, 'gnf': 57, 'gtq': 58, 'gyd': 59, 'hkd': 60, 'hnl': 61, 'hrk': 62, 'htg': 63, 'huf': 64, 'idr': 65, 'ils': 66, 'imp': 67, 'inr': 68, 'iqd': 69, 'irr': 70, 'isk': 71, 'jep': 72, 'jmd': 73, 'jod': 74, 'jpy': 75, 'kes': 76, 'kgs': 77, 'khr': 78, 'kmf': 79, 'kpw': 80, 'krw': 81, 'kwd': 82, 'kyd': 83, 'kzt': 84, 'lak': 85, 'lbp': 86, 'lkr': 87, 'lrd': 88, 'lsl': 89, 'ltl': 90, 'lvl': 91, 'lyd': 92, 'mad': 93, 'mdl': 94, 'mga': 95, 'mkd': 96, 'mmk': 97, 'mnt': 98, 'mop': 99, 'mro': 100, 'mur': 101, 'mvr': 102, 'mwk': 103, 'mxn': 104, 'myr': 105, 'mzn': 106, 'nad': 107, 'ngn': 108, 'nio': 109, 'nok': 110, 'npr': 111, 'nzd': 112, 'omr': 113, 'pab': 114, 'pen': 115, 'pgk': 116, 'php': 117, 'pkr': 118, 'pln': 119, 'pyg': 120, 'qar': 121, 'ron': 122, 'rsd': 123, 'rub': 124, 'rwf': 125, 'sar': 126, 'sbd': 127, 'scr': 128, 'sdg': 129, 'sek': 130, 'sgd': 131, 'shp': 132, 'sll': 133, 'sos': 134, 'srd': 135, 'std': 136, 'svc': 137, 'syp': 138, 'szl': 139, 'thb': 140, 'tjs': 141, 'tmt': 142, 'tnd': 143, 'top': 144, 'try': 145, 'ttd': 146, 'twd': 147, 'tzs': 148, 'uah': 149, 'ugx': 150, 'usd': 151, 'uyu': 152, 'uzs': 153, 'vef': 154, 'vnd': 155, 'vuv': 156, 'wst': 157, 'xaf': 158, 'xag': 159, 'xau': 160, 'xcd': 161, 'xdr': 162, 'xof': 163, 'xpf': 164, 'xrp': 165, 'yer': 166, 'zar': 167, 'zmk': 168, 'zmw': 169, 'zwl': 170}
    -
    -JS-NG>
    -JS-NG> fake.api_key="VALID KEY"
    -JS-NG> j.clients.currencylayer.fake.load()
    -JS-NG> j.clients.currencylayer.fake.id2cur_print()
    -{1: 'aed',
    - 2: 'afn',
    - 3: 'all',
    - 4: 'amd',
    - 5: 'ang',
    - 6: 'aoa',
    - 7: 'ars',
    - 8: 'aud',
    - 9: 'awg',
    - 10: 'azn',
    - 11: 'bam',
    - 12: 'bbd',
    - 13: 'bdt',
    - 14: 'bgn',
    - 15: 'bhd',
    - 16: 'bif',
    - 17: 'bmd',
    - 18: 'bnd',
    - 19: 'bob',
    - 20: 'brl',
    - 21: 'bsd',
    - 22: 'btc',
    - 23: 'btn',
    - 24: 'bwp',
    - 25: 'byn',
    - 26: 'byr',
    - 27: 'bzd',
    - 28: 'cad',
    - 29: 'cdf',
    - 30: 'chf',
    - 31: 'clf',
    - 32: 'clp',
    - 33: 'cny',
    - 34: 'cop',
    - 35: 'crc',
    - 36: 'cuc',
    - 37: 'cup',
    - 38: 'cve',
    - 39: 'czk',
    - 40: 'djf',
    - 41: 'dkk',
    - 42: 'dop',
    - 43: 'dzd',
    - 44: 'egp',
    - 45: 'ern',
    - 46: 'etb',
    - 47: 'eth',
    - 48: 'eur',
    - 49: 'fjd',
    - 50: 'fkp',
    - 51: 'gbp',
    - 52: 'gel',
    - 53: 'ggp',
    - 54: 'ghs',
    - 55: 'gip',
    - 56: 'gmd',
    - 57: 'gnf',
    - 58: 'gtq',
    - 59: 'gyd',
    - 60: 'hkd',
    - 61: 'hnl',
    - 62: 'hrk',
    - 63: 'htg',
    - 64: 'huf',
    - 65: 'idr',
    - 66: 'ils',
    - 67: 'imp',
    - 68: 'inr',
    - 69: 'iqd',
    - 70: 'irr',
    - 71: 'isk',
    - 72: 'jep',
    - 73: 'jmd',
    - 74: 'jod',
    - 75: 'jpy',
    - 76: 'kes',
    - 77: 'kgs',
    - 78: 'khr',
    - 79: 'kmf',
    - 80: 'kpw',
    - 81: 'krw',
    - 82: 'kwd',
    - 83: 'kyd',
    - 84: 'kzt',
    - 85: 'lak',
    - 86: 'lbp',
    - 87: 'lkr',
    - 88: 'lrd',
    - 89: 'lsl',
    - 90: 'ltl',
    - 91: 'lvl',
    - 92: 'lyd',
    - 93: 'mad',
    - 94: 'mdl',
    - 95: 'mga',
    - 96: 'mkd',
    - 97: 'mmk',
    - 98: 'mnt',
    - 99: 'mop',
    - 100: 'mro',
    - 101: 'mur',
    - 102: 'mvr',
    - 103: 'mwk',
    - 104: 'mxn',
    - 105: 'myr',
    - 106: 'mzn',
    - 107: 'nad',
    - 108: 'ngn',
    - 109: 'nio',
    - 110: 'nok',
    - 111: 'npr',
    - 112: 'nzd',
    - 113: 'omr',
    - 114: 'pab',
    - 115: 'pen',
    - 116: 'pgk',
    - 117: 'php',
    - 118: 'pkr',
    - 119: 'pln',
    - 120: 'pyg',
    - 121: 'qar',
    - 122: 'ron',
    - 123: 'rsd',
    - 124: 'rub',
    - 125: 'rwf',
    - 126: 'sar',
    - 127: 'sbd',
    - 128: 'scr',
    - 129: 'sdg',
    - 130: 'sek',
    - 131: 'sgd',
    - 132: 'shp',
    - 133: 'sll',
    - 134: 'sos',
    - 135: 'srd',
    - 136: 'std',
    - 137: 'svc',
    - 138: 'syp',
    - 139: 'szl',
    - 140: 'thb',
    - 141: 'tjs',
    - 142: 'tmt',
    - 143: 'tnd',
    - 144: 'top',
    - 145: 'try',
    - 146: 'ttd',
    - 147: 'twd',
    - 148: 'tzs',
    - 149: 'uah',
    - 150: 'ugx',
    - 151: 'usd',
    - 152: 'uyu',
    - 153: 'uzs',
    - 154: 'vef',
    - 155: 'vnd',
    - 156: 'vuv',
    - 157: 'wst',
    - 158: 'xaf',
    - 159: 'xag',
    - 160: 'xau',
    - 161: 'xcd',
    - 162: 'xdr',
    - 163: 'xof',
    - 164: 'xpf',
    - 165: 'xrp',
    - 166: 'yer',
    - 167: 'zar',
    - 168: 'zmk',
    - 169: 'zmw',
    - 170: 'zwl'}
    -JS-NG> j.clients.currencylayer.fake.cur2usd_print()
    -{'': 1,
    - 'aed': 3.672979,
    - 'afn': 78.296617,
    - 'ah': 24.914996,
    - 'all': 109.150047,
    - 'amd': 476.210221,
    - 'ang': 1.78525,
    - 'aoa': 362.0025,
    - 'ar': 3.75045,
    - 'ars': 55.394992,
    - 'aud': 1.474703,
    - 'awg': 1.8,
    - 'azn': 1.704964,
    - 'bam': 1.758993,
    - 'bbd': 2.0194,
    - 'bd': 8.221403,
    - 'bdt': 83.745499,
    - 'bgn': 1.760801,
    - 'bhd': 0.375961,
    - 'bif': 1855,
    - 'bmd': 1,
    - 'bnd': 1.350696,
    - 'bob': 6.86065,
    - 'brl': 4.152695,
    - 'bsd': 0.99205,
    - 'btc': 9.948852946999476e-05,
    - 'btn': 71.884502,
    - 'bwp': 10.961999,
    - 'byn': 2.060501,
    - 'byr': 19600,
    - 'bzd': 2.01595,
    - 'cad': 1.32733,
    - 'cdf': 1659.99946,
    - 'chf': 0.978545,
    - 'clf': 0.026094,
    - 'clp': 720.00501,
    - 'cny': 7.151304,
    - 'cop': 3431.55,
    - 'cr': 13.669974,
    - 'crc': 567.080062,
    - 'cuc': 1,
    - 'cup': 26.5,
    - 'cve': 98.749501,
    - 'czk': 23.208988,
    - 'egp': 16.53602,
    - 'ek': 9.67235,
    - 'ern': 14.999484,
    - 'etb': 29.000284,
    - 'eth': 0.005395489370885939,
    - 'eur': 0.900035,
    - 'fjd': 2.17495,
    - 'fkp': 0.81691,
    - 'g': 45.119039,
    - 'gbp': 0.81752,
    - 'gd': 1.38792,
    - 'gel': 2.925034,
    - 'ggp': 0.81764,
    - 'ghs': 5.402501,
    - 'gip': 0.81691,
    - 'gmd': 50.415037,
    - 'gnf': 9239.999966,
    - 'gtq': 7.680957,
    - 'gx': 3685.496424,
    - 'gyd': 209.244968,
    - 'hkd': 7.84595,
    - 'hnl': 24.674984,
    - 'hp': 1.320898,
    - 'hrk': 6.653399,
    - 'htg': 95.361503,
    - 'huf': 296.280997,
    - 'idr': 14258.25,
    - 'ils': 3.52095,
    - 'imp': 0.81764,
    - 'inr': 71.792403,
    - 'iqd': 1190,
    - 'irr': 42104.999481,
    - 'isk': 124.829491,
    - 'jep': 0.81764,
    - 'jf': 177.720165,
    - 'jmd': 134.559965,
    - 'jod': 0.7084,
    - 'jpy': 106.015996,
    - 'kes': 103.389937,
    - 'kgs': 69.8159,
    - 'khr': 4140.000279,
    - 'kk': 6.71151,
    - 'kmf': 443.249767,
    - 'kpw': 900.052015,
    - 'krw': 1214.824979,
    - 'kwd': 0.303901,
    - 'kyd': 0.83355,
    - 'kzt': 383.110385,
    - 'lak': 8735.000017,
    - 'lbp': 1507.949729,
    - 'lkr': 179.605474,
    - 'll': 9299.999946,
    - 'lrd': 205.000232,
    - 'lsl': 15.250149,
    - 'ltl': 2.95274,
    - 'lvl': 0.60489,
    - 'lyd': 1.40503,
    - 'mad': 9.5685,
    - 'mdl': 17.887498,
    - 'mga': 3674.999563,
    - 'mkd': 55.324023,
    - 'mmk': 1516.702673,
    - 'mnt': 2669.391245,
    - 'mop': 8.080496,
    - 'mro': 357.000024,
    - 'mur': 36.043506,
    - 'mvr': 15.410297,
    - 'mwk': 731.210149,
    - 'mxn': 19.92145,
    - 'myr': 4.198897,
    - 'mzn': 61.020166,
    - 'nad': 15.270055,
    - 'ngn': 362.000148,
    - 'nio': 33.602406,
    - 'nok': 8.988065,
    - 'npr': 115.010199,
    - 'nzd': 1.56365,
    - 'omr': 0.384976,
    - 'op': 51.294983,
    - 'os': 579.999893,
    - 'pab': 0.99205,
    - 'pen': 3.37635,
    - 'pgk': 3.397801,
    - 'php': 52.438012,
    - 'pkr': 157.249855,
    - 'pln': 3.92254,
    - 'pyg': 6217.103241,
    - 'qar': 3.64175,
    - 'rd': 7.457963,
    - 'ron': 4.256202,
    - 'rsd': 106.069758,
    - 'rub': 66.06102,
    - 'rwf': 910,
    - 'td': 21560.79,
    - 'thb': 30.589849,
    - 'tjs': 9.696302,
    - 'tmt': 3.5,
    - 'tnd': 2.857701,
    - 'top': 2.320597,
    - 'try': 5.81132,
    - 'ttd': 6.71695,
    - 'twd': 31.400972,
    - 'tzs': 2298.149889,
    - 'vc': 8.75195,
    - 'vef': 9.987501,
    - 'vnd': 23199,
    - 'vuv': 117.90362,
    - 'wst': 2.675215,
    - 'xaf': 589.959986,
    - 'xag': 0.056555,
    - 'xau': 0.000653,
    - 'xcd': 2.70245,
    - 'xdr': 0.729108,
    - 'xof': 584.499865,
    - 'xpf': 106.950279,
    - 'xrp': 3.771,
    - 'yer': 250.349819,
    - 'yp': 515.000236,
    - 'yu': 36.34003,
    - 'zar': 15.26498,
    - 'zd': 119.879946,
    - 'zl': 15.269489,
    - 'zmk': 9001.202171,
    - 'zmw': 13.112024,
    - 'zs': 9376.306597,
    - 'zwl': 322.000001}
    -
    -
    -"""
    -
    -
    -def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .currencylayer import CurrencyLayerClient
    -    return StoredFactory(CurrencyLayerClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.currencylayer.currencies
    -
    -
    -
    -
    jumpscale.clients.currencylayer.currencylayer
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .currencylayer import CurrencyLayerClient
    -    return StoredFactory(CurrencyLayerClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/digitalocean/digitalocean.html b/docs/api/jumpscale/clients/digitalocean/digitalocean.html deleted file mode 100644 index d49353961..000000000 --- a/docs/api/jumpscale/clients/digitalocean/digitalocean.html +++ /dev/null @@ -1,2822 +0,0 @@ - - - - - - -jumpscale.clients.digitalocean.digitalocean API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.digitalocean.digitalocean

    -
    -
    -

    This module is used to manage your digital ocean account, create droplet,list all the droplets, destroy droplets, create project, list all the projects and delete project

    -

    prerequisites

    -
    ## Sshkey client and loaded with you public key
    -
    -

    '''python -ssh = j.clients.sshkey.get(name= "test") -ssh.private_key_path = "/home/rafy/.ssh/id_rsa" -ssh.load_from_file_system() -'''

    -

    Create Digital Ocean client and set your token and load your sshkey

    -
    dg = j.clients.digitalocean.get("testDG")
    -dg.token_ = ""
    -
    -

    Set sshclient you have created

    -
    dg.set_default_sshkey(ssh)
    -
    -

    Use Digital Ocean client

    -

    Create and deploy Project to Digital Ocean

    -

    Create Project (name must not contian spaces or start with number)

    -
     project = dg.projects.new("test_DG_client")
    -
    -

    Set the name you want to deploy with on Digital Ocean

    -
    project.set_digital_ocean_name("test project DO client")
    -
    -

    Deploy the project (you have to specific the purpose)

    -
    project.deploy(purpose="testing digital ocean client")
    -
    -

    Delete the project from Digital Ocean

    -
    project.delete_remote()
    -
    -

    Create and deploy Droplet to Digital Ocean

    -

    Create Droplet (name must not contian spaces or start with number)

    -
    droplet = dg.droplets.new("test_droplet_dg")
    -
    -

    Set the name you want to deploy with on Digital Ocean

    -
    droplet.set_digital_ocean_name("droplet-test-DO")
    -
    -

    Deploy the Droplet

    -
    droplet.deploy()
    -
    -

    Delete the Droplet from Digital Ocean

    -
    droplet.delete_remote()
    -
    -

    Get digital ocean regions

    -
    dg.regions
    -
    -

    Get digital ocean images

    -
    dg.images
    -
    -

    In the below examples, I have supposed that you followed the above steps -and you got digital ocean client with the name (dg)

    -
    -Source code -
    """This module is used to manage your digital ocean account, create droplet,list all the droplets, destroy droplets, create project, list all the projects and delete project
    -# prerequisites
    -    ## Sshkey client and loaded with you public key
    -'''python
    -ssh = j.clients.sshkey.get(name= "test")
    -ssh.private_key_path = "/home/rafy/.ssh/id_rsa"
    -ssh.load_from_file_system()
    -'''
    -## Create Digital Ocean client and set your token and load your sshkey
    -```python
    -dg = j.clients.digitalocean.get("testDG")
    -dg.token_ = ""
    -```
    -## Set sshclient you have created
    -``` python
    -dg.set_default_sshkey(ssh)
    -```
    -## Use Digital Ocean client
    -
    -### Create and deploy Project to Digital Ocean
    -
    -#### Create Project (name must not contian spaces or start with number)
    -``` python
    - project = dg.projects.new("test_DG_client")
    -```
    -#### Set the name you want to deploy with on Digital Ocean
    -``` python
    -project.set_digital_ocean_name("test project DO client")
    -```
    -#### Deploy the project (you have to specific the purpose)
    -``` python
    -project.deploy(purpose="testing digital ocean client")
    -```
    -#### Delete the project from Digital Ocean
    -``` python
    -project.delete_remote()
    -```
    -
    -### Create and deploy Droplet to Digital Ocean
    -
    -#### Create Droplet (name must not contian spaces or start with number)
    -``` python
    -droplet = dg.droplets.new("test_droplet_dg")
    -```
    -#### Set the name you want to deploy with on Digital Ocean
    -``` python
    -droplet.set_digital_ocean_name("droplet-test-DO")
    -```
    -#### Deploy the Droplet
    -```python
    -droplet.deploy()
    -```
    -#### Delete the Droplet from Digital Ocean
    -```python
    -droplet.delete_remote()
    -```
    -
    -### Get digital ocean regions
    -```python
    -dg.regions
    -```
    -
    -### Get digital ocean images
    -```python
    -dg.images
    -```
    -
    -In the below examples, I have supposed that you followed the above steps
    -and you got digital ocean client with the name (dg)
    -"""
    -
    -from jumpscale.loader import j
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -from jumpscale.core.base import StoredFactory
    -from .project import ProjectManagement
    -import digitalocean
    -
    -
    -class ProjectFactory(StoredFactory):
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -
    -    def list_remote(self):
    -        """
    -        Returns list of projects on Digital Ocean
    -
    -        e.g
    -            dg.projects.list_remote()  -> list of projects
    -
    -        Returns
    -            list(projects): list of projects on digital ocean
    -
    -        """
    -        return ProjectManagement.list(self.parent_instance.client)
    -
    -    def check_project_exist_remote(self, name):
    -        """
    -        Check a project with specific name exits on Digital Ocean
    -
    -        e.g
    -            dg.projects.check_project_exist_remote("codescalers")  -> True
    -            dg.projects.check_project_exist_remote("dsrfjsdfjl")  -> False
    -
    -        Args
    -            name (str): name of the project
    -
    -        Returns
    -            bool : True if the project exits and False if the project does not exist on digital ocean
    -        """
    -        for project in self.list_remote():
    -            if project.name == name:
    -                return True
    -        return False
    -
    -    def get_project_exist_remote(self, name):
    -        """
    -        Get a project with specifc name from  Digital Ocean.
    -
    -        e.g
    -            dg.projects.get_project_exist_remote("codescalers")  -> project
    -
    -        Args
    -            name (str): name of the project
    -
    -        Returns
    -            Project : a project from digital ocean with the name specified
    -        """
    -        for project in self.list_remote():
    -            if project.name == name:
    -                return project
    -        raise j.exceptions.Input("could not find project with name:%s on you Digital Ocean account" % name)
    -
    -
    -class Project(Client):
    -    do_name = fields.String()
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -
    -    def set_digital_ocean_name(self, name):
    -        """Set a name for your project to be used on Digital Ocean
    -        e.g
    -            project.set_digital_ocean_name("test project DO client")
    -
    -        Args:
    -            name (str): name to be used on digital ocean
    -        """
    -        self.do_name = name
    -
    -    def get_digital_ocean_name(self):
    -        """Get a name for the project which is used on digital ocean
    -        e.g
    -            project.get_digital_ocean_name()  ->  "test project DO client"
    -
    -        Returns:
    -            str: name for the project which is used on digital ocean
    -        """
    -        return self.do_name
    -
    -    def deploy(self, purpose, description="", environment="", is_default=False):
    -        """Create a digital ocean project
    -        e.g
    -            project.deploy(purpose="testing digital ocean client")  -> project
    -        Args:
    -            purpose(str): purpose of the project (not optional)
    -            description(str): description of the project, defaults to ""
    -            environment(str): environment of project's resources, defaults to ""
    -            is_default(bool): make this the default project for your user
    -
    -        Returns:
    -            project: The project object that has been created
    -        """
    -
    -        if self.parent.projects.check_project_exist_remote(self.do_name):
    -            raise j.exceptions.Value("A project with the same name already exists")
    -
    -        project = ProjectManagement(
    -            token=self.parent.projects.parent_instance.token_,
    -            name=self.do_name,
    -            purpose=purpose,
    -            description=description,
    -            environment=environment,
    -            is_default=is_default,
    -        )
    -        project.create()
    -
    -        if is_default:
    -            project.update(is_default=True)
    -
    -        return project
    -
    -    def delete_remote(self):
    -        """Delete the project from Digital Ocean (A project can't be deleted unless it has no resources.)
    -
    -        e.g
    -            project.delete_remote()
    -        """
    -        project = self.parent.projects.get_project_exist_remote(self.do_name)
    -        project.delete()
    -
    -
    -class DropletFactory(StoredFactory):
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -
    -    def list_remote(self, project_name=None):
    -        """
    -        List all remote droplet or list droplets for a project if it is specified
    -
    -        e.g
    -            dg.droplets.list_remote()  -> list of droplets
    -            dg.droplets.list_remote("codescalers")  -> list of droplets on codescalers project
    -
    -        Args:
    -            project_name (str) : name of project on digital ocean (optional)
    -
    -        Returns
    -            list (Droplet) : list of droplets on digital ocean
    -
    -
    -        """
    -        if project_name:
    -            project = self.parent_instance.projects.get_project_exist_remote(project_name)
    -            return project.list_droplets()
    -
    -        return self.parent_instance.client.get_all_droplets()
    -
    -    def check_droplet_exist_remote(self, name):
    -        """
    -        Check droplet exists on digital ocean
    -
    -        e.g
    -            dg.droplets.check_droplet_exist_remote("3git")  -> True
    -            dg.droplets.check_droplet_exist_remote("sdfgdfed")  -> False
    -
    -        Args:
    -            name (str) : name of droplet
    -
    -        Returns
    -            bool : True if the droplet exist or False if the droplet does not exist
    -        """
    -        for droplet in self.list_remote():
    -            if droplet.name.lower() == name.lower():
    -                return True
    -        return False
    -
    -    def get_droplet_exist_remote(self, name):
    -        """
    -        Get Droplet exists from Digital Ocean
    -
    -        e.g
    -            dg.droplets.get_droplet_exist_remote("3git")
    -
    -        Args:
    -            name (str) : name of droplet
    -
    -        Returns
    -            droplet : droplet with the name specified
    -
    -        """
    -        for droplet in self.list_remote():
    -            if droplet.name.lower() == name.lower():
    -                return droplet
    -        raise j.exceptions.Input("could not find project with name:%s on you Digital Ocean account" % name)
    -
    -    def shutdown_all(self, project_name=None):
    -        """
    -        Shutdown all the droplets or droplets in specific project
    -
    -        e.g
    -            dg.droplets.shutdown_all("codescalers")
    -            dg.droplets.shutdown_all()
    -
    -        Args:
    -            name (str) : name of the project
    -
    -        """
    -        for droplet in self.list_remote(project_name):
    -            droplet.shutdown()
    -
    -    def delete_all(self, ignore=None, interactive=True, project_name=None):
    -        """
    -        Delete all the droplets or delete all the droplets in specific project
    -
    -        e.g
    -            dg.droplets.delete_all(project_name = "codescalers")
    -            dg.droplets.delete_all()
    -
    -        Args:
    -            project_name (str) : name of the project
    -            ignore (list): list of ignored droplets to prevent their deletion
    -            interactive (bool): if True the deletion will be interactive and
    -                                confirm if you want to delete but if False it
    -                                will delete directly
    -        """
    -        if not ignore:
    -            ignore = []
    -
    -        def test(ignore, name):
    -            if name.startswith("TF-"):
    -                return False
    -            for item in ignore:
    -                if name.lower().find(item.lower()) != -1:
    -                    return False
    -            return True
    -
    -        todo = []
    -        for droplet in self.list_remote(project_name):
    -            if test(ignore, droplet.name):
    -                todo.append(droplet)
    -        if todo != []:
    -            todotxt = ",".join([i.name for i in todo])
    -            if not interactive or j.tools.console.ask_yes_no("ok to delete:%s" % todotxt):
    -                for droplet in todo:
    -                    droplet.destroy()
    -
    -
    -class Droplet(Client):
    -    do_name = fields.String()
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -
    -    def set_digital_ocean_name(self, name):
    -        """Set a name for your Droplet to be used on Digital Ocean
    -
    -        e.g
    -            droplet.set_digital_ocean_name("test-name")
    -
    -        Args:
    -            name (str): name to be used on digital ocean
    -        """
    -        self.do_name = name
    -
    -    def get_digital_ocean_name(self):
    -        """Get a name for the Droplet which is used on digital ocean
    -
    -        e.g
    -            droplet.get_digital_ocean_name()  ->  "test-name"
    -
    -        Returns
    -            str: name for the droplet which is used on digital ocean
    -        """
    -        return self.do_name
    -
    -    def deploy(
    -        self,
    -        sshkey=None,
    -        region="Amsterdam 3",
    -        image="ubuntu 18.04",
    -        size_slug="s-1vcpu-2gb",
    -        delete=True,
    -        project_name=None,
    -    ):
    -        """
    -        Deploy your Droplet to Digital Ocean
    -
    -        Args
    -            sshkey (string): sshkey name used on digital ocean (if not set it will use the default one which already loaded)
    -            region (string): region name to deploy to
    -            image (string): Image name to be used
    -            size_slug (string): size of the droplet  (s-1vcpu-2gb,s-6vcpu-16gb,gd-8vcpu-32gb)
    -            delete (bool): delete the droplet if it is already deployed on digital ocean
    -            project_name (string): project to add this droplet it. If not specified the default project will be used.
    -
    -        """
    -        project = None
    -        if project_name:
    -            project = self.parent.projects.get_project_exist_remote(self.do_name)
    -            if not project:
    -                raise j.exceptions.Input("could not find project with name:%s" % project_name)
    -
    -        # Get ssh
    -        if not sshkey:
    -            sshkey_do = self.parent.get_default_sshkey()
    -
    -            if not sshkey_do:
    -                # means we did not find the sshkey on digital ocean yet, need to create
    -                sshkey = self.parent.sshkey
    -                key = digitalocean.SSHKey(
    -                    token=self.parent.projects.parent_instance.token_, name=sshkey.name, public_key=sshkey.public_key
    -                )
    -                key.create()
    -            sshkey_do = self.parent.get_default_sshkey()
    -            assert sshkey_do
    -            sshkey = sshkey_do.name
    -
    -        if self.parent.droplets.check_droplet_exist_remote(self.do_name):
    -            dr0 = self.parent.droplets.get_droplet_exist_remote(self.do_name)
    -            if delete:
    -                dr0.destroy()
    -            else:
    -                sshcl = j.clients.sshclient.get(name="do_%s" % self.do_name, host=dr0.ip_address, sshkey=sshkey)
    -                return dr0, sshcl
    -
    -        sshkey = self.parent.droplets.parent_instance.get_sshkey(sshkey)
    -        region = self.parent.droplets.parent_instance.get_region(region)
    -        imagedo = self.parent.droplets.parent_instance.get_image(image)
    -
    -        img_slug_or_id = imagedo.slug if imagedo.slug else imagedo.id
    -
    -        droplet = digitalocean.Droplet(
    -            token=self.parent.droplets.parent_instance.token_,
    -            name=self.do_name,
    -            region=region.slug,
    -            image=img_slug_or_id,
    -            size_slug=size_slug,
    -            ssh_keys=[sshkey],
    -            backups=False,
    -        )
    -        droplet.create()
    -
    -        if project:
    -            project.assign_resources(["do:droplet:%s" % droplet.id])
    -
    -    def delete_remote(self):
    -        """Delete Droplet from digital ocean
    -
    -        e.g
    -            droplet.delete_remote()
    -
    -        """
    -        droplet = self.parent.droplets.get_droplet_exist_remote(self.do_name)
    -        droplet.destroy()
    -
    -
    -class DigitalOcean(Client):
    -    name = fields.String()
    -    token_ = fields.String()
    -    projects = fields.Factory(Project, factory_type=ProjectFactory)
    -    droplets = fields.Factory(Droplet, factory_type=DropletFactory)
    -
    -    def __init__(self):
    -        super().__init__()
    -        self._client = None
    -
    -    @property
    -    def client(self):
    -        """Return a new client if it is not set or it will return the already existed one
    -
    -        e.g
    -            dg.client  -> <Manager>
    -        Returns
    -            Manager: client form digital ocean manager
    -        """
    -
    -        if not self._client:
    -            self._client = digitalocean.Manager(token=self.token_)
    -        return self._client
    -
    -    # Images
    -    @property
    -    def images(self):
    -        """Return a list of digital ocean availabe images
    -
    -        e.g
    -            dg.images  -> [<Image: 31354013 CentOS 6.9 x32>,
    -                                         <Image: 34902021 CentOS 6.9 x64>,...]
    -
    -        Returns
    -            List : list of images on digital ocean available
    -        """
    -        return self.client.get_distro_images()
    -
    -    @property
    -    def myimages(self):
    -        """Return a list of digital ocean images, you have created
    -
    -        e.g
    -            dg.myimages  -> [<Image: 48614453 Unknown Zero_OS>,
    -                                        <Image: 50898718 Ubuntu JumpScale>,...]
    -
    -        Returns
    -            List : list of images on digital ocean, you have created
    -        """
    -        return self.client.get_images(private=True)
    -
    -    @property
    -    def account_images(self):
    -        """Return a list of digital ocean images and the images you have created
    -
    -        e.g
    -            dg.account_images  -> [<Image: 31354013 CentOS 6.9 x32>,
    -                                             <Image: 34902021 CentOS 6.9 x64>,...]
    -
    -        Returns
    -            List : list of images on digital ocean images and the images you have created
    -        """
    -
    -        return self.images + self.myimages
    -
    -    def get_image(self, name):
    -        """Return an image
    -
    -        e.g
    -            dg.get_image(name="CentOS")  -> <Image: 31354013 CentOS 6.9 x32>
    -
    -        Args
    -            name (str): name of the  required image
    -        Returns
    -            Image : list of images on digital ocean images and the images you have created
    -        """
    -        for item in self.account_images:
    -            if item.description:
    -                name_do1 = item.description.lower()
    -            else:
    -                name_do1 = ""
    -            name_do2 = item.distribution + " " + item.name
    -            print(f" - {name_do1}--{name_do2}")
    -            if name_do1.lower().find(name.lower()) != -1 or name_do2.lower().find(name.lower()) != -1:
    -                return item
    -        raise j.exceptions.Base("did not find image:%s" % name)
    -
    -    def get_image_names(self, name=""):
    -        """ Return all the image  or images with a specified name
    -         e.g
    -            dg.get_image_names()  -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602',...]
    -            dg.get_image_names("centos") -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602']
    -
    -        Args
    -            name (str): name of the  required image
    -        Returns
    -            Image : list of images
    -        """
    -        res = []
    -        name = name.lower()
    -        for item in self.images:
    -            if item.description:
    -                name_do = item.description.lower()
    -            else:
    -                name_do = item.distribution + " " + item.name
    -            if name_do.find(name) != -1:
    -                res.append(name_do)
    -        return res
    -
    -    # Size
    -
    -    @property
    -    def sizes(self):
    -        """Return a list sizes available on digital ocean
    -
    -        e.g
    -            dg.sizes -> [s-1vcpu-1gb, 512mb, s-1vcpu-2gb, 1gb, s-3vcpu-1gb,.....]
    -
    -        Returns
    -            List : list of sizes
    -        """
    -        return self.client.get_all_sizes()
    -
    -    # Regions
    -
    -    @property
    -    def regions(self):
    -        """Return a list regions available on digital ocean
    -
    -        e.g
    -            dg.regions  -> [<Region: nyc1 New York 1>, <Region: sgp1 Singapore 1>,...]
    -
    -        Returns
    -            List : list of regions
    -        """
    -        return self.client.get_all_regions()
    -
    -    @property
    -    def region_names(self):
    -        """Returns Digital Ocean regions
    -
    -        e.g
    -            dg.region_names  -> ['nyc1', 'sgp1', 'lon1', 'nyc3', 'ams3', 'fra1', 'tor1', 'sfo2', 'blr1']
    -
    -        Returns
    -            list : list of digital ocean regions
    -        """
    -        return [i.slug for i in self.regions]
    -
    -    def get_region(self, name):
    -        """
    -        Returns specific region
    -
    -        e.g
    -            dg.get_region(name = 'nyc1')  -> <Region: nyc1 New York 1>
    -
    -        Args
    -            name (str) : name of the required region
    -        Returns
    -            Region : the region with the name specified
    -        """
    -        for item in self.regions:
    -            if name == item.slug:
    -                return item
    -            if name == item.name:
    -                return item
    -        raise j.exceptions.Base("did not find region:%s" % name)
    -
    -    # SSHkeys
    -    @property
    -    def sshkeys(self):
    -        """
    -        Return list of sshkeys on digital ocean
    -
    -        e.g
    -            dg.sshkeys  -> [<SSHKey: 25882170 3bot_container_sandbox>,
    -                             <SSHKey: 27130645 Geert-root>,...]
    -
    -        Returns
    -            list : list of sshkeys
    -        """
    -
    -        return self.client.get_all_sshkeys()
    -
    -    def get_default_sshkey(self):
    -        """
    -        Return sshkey you have added to your Digital Ocean client
    -
    -        e.g
    -            dg.get_default_sshkey()  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -        Returns
    -            list : list of sshkeys
    -        """
    -        pubkeyonly = self.sshkey.public_key
    -        for item in self.sshkeys:
    -            if item.public_key.find(pubkeyonly) != -1:
    -                return item
    -        return None
    -
    -    def set_default_sshkey(self, default_sshkey):
    -        """
    -        Set sshkey you  Digital Ocean client
    -
    -        e.g
    -            dg.set_default_sshkey(ssh)  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -        Args
    -            default_sshkey (SSHKeyClient) : sshkey client you have created
    -        """
    -        self.sshkey = default_sshkey
    -
    -    def get_sshkey(self, name):
    -        """
    -        get sshkey from  Digital Ocean
    -
    -        e.g
    -            dg.get_sshkey("rafy@rafy-Inspiron-3576")   ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -        Args
    -            name (string) : sshkey name
    -
    -        Returns
    -            SSHKey : return the specified sshkey
    -        """
    -        for item in self.sshkeys:
    -            if name == item.name:
    -                return item
    -        raise j.exceptions.Base("did not find key:%s" % name)
    -
    -    def __str__(self):
    -        return "digital ocean client:%s" % self.name
    -
    -    __repr__ = __str__
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class DigitalOcean -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class DigitalOcean(Client):
    -    name = fields.String()
    -    token_ = fields.String()
    -    projects = fields.Factory(Project, factory_type=ProjectFactory)
    -    droplets = fields.Factory(Droplet, factory_type=DropletFactory)
    -
    -    def __init__(self):
    -        super().__init__()
    -        self._client = None
    -
    -    @property
    -    def client(self):
    -        """Return a new client if it is not set or it will return the already existed one
    -
    -        e.g
    -            dg.client  -> <Manager>
    -        Returns
    -            Manager: client form digital ocean manager
    -        """
    -
    -        if not self._client:
    -            self._client = digitalocean.Manager(token=self.token_)
    -        return self._client
    -
    -    # Images
    -    @property
    -    def images(self):
    -        """Return a list of digital ocean availabe images
    -
    -        e.g
    -            dg.images  -> [<Image: 31354013 CentOS 6.9 x32>,
    -                                         <Image: 34902021 CentOS 6.9 x64>,...]
    -
    -        Returns
    -            List : list of images on digital ocean available
    -        """
    -        return self.client.get_distro_images()
    -
    -    @property
    -    def myimages(self):
    -        """Return a list of digital ocean images, you have created
    -
    -        e.g
    -            dg.myimages  -> [<Image: 48614453 Unknown Zero_OS>,
    -                                        <Image: 50898718 Ubuntu JumpScale>,...]
    -
    -        Returns
    -            List : list of images on digital ocean, you have created
    -        """
    -        return self.client.get_images(private=True)
    -
    -    @property
    -    def account_images(self):
    -        """Return a list of digital ocean images and the images you have created
    -
    -        e.g
    -            dg.account_images  -> [<Image: 31354013 CentOS 6.9 x32>,
    -                                             <Image: 34902021 CentOS 6.9 x64>,...]
    -
    -        Returns
    -            List : list of images on digital ocean images and the images you have created
    -        """
    -
    -        return self.images + self.myimages
    -
    -    def get_image(self, name):
    -        """Return an image
    -
    -        e.g
    -            dg.get_image(name="CentOS")  -> <Image: 31354013 CentOS 6.9 x32>
    -
    -        Args
    -            name (str): name of the  required image
    -        Returns
    -            Image : list of images on digital ocean images and the images you have created
    -        """
    -        for item in self.account_images:
    -            if item.description:
    -                name_do1 = item.description.lower()
    -            else:
    -                name_do1 = ""
    -            name_do2 = item.distribution + " " + item.name
    -            print(f" - {name_do1}--{name_do2}")
    -            if name_do1.lower().find(name.lower()) != -1 or name_do2.lower().find(name.lower()) != -1:
    -                return item
    -        raise j.exceptions.Base("did not find image:%s" % name)
    -
    -    def get_image_names(self, name=""):
    -        """ Return all the image  or images with a specified name
    -         e.g
    -            dg.get_image_names()  -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602',...]
    -            dg.get_image_names("centos") -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602']
    -
    -        Args
    -            name (str): name of the  required image
    -        Returns
    -            Image : list of images
    -        """
    -        res = []
    -        name = name.lower()
    -        for item in self.images:
    -            if item.description:
    -                name_do = item.description.lower()
    -            else:
    -                name_do = item.distribution + " " + item.name
    -            if name_do.find(name) != -1:
    -                res.append(name_do)
    -        return res
    -
    -    # Size
    -
    -    @property
    -    def sizes(self):
    -        """Return a list sizes available on digital ocean
    -
    -        e.g
    -            dg.sizes -> [s-1vcpu-1gb, 512mb, s-1vcpu-2gb, 1gb, s-3vcpu-1gb,.....]
    -
    -        Returns
    -            List : list of sizes
    -        """
    -        return self.client.get_all_sizes()
    -
    -    # Regions
    -
    -    @property
    -    def regions(self):
    -        """Return a list regions available on digital ocean
    -
    -        e.g
    -            dg.regions  -> [<Region: nyc1 New York 1>, <Region: sgp1 Singapore 1>,...]
    -
    -        Returns
    -            List : list of regions
    -        """
    -        return self.client.get_all_regions()
    -
    -    @property
    -    def region_names(self):
    -        """Returns Digital Ocean regions
    -
    -        e.g
    -            dg.region_names  -> ['nyc1', 'sgp1', 'lon1', 'nyc3', 'ams3', 'fra1', 'tor1', 'sfo2', 'blr1']
    -
    -        Returns
    -            list : list of digital ocean regions
    -        """
    -        return [i.slug for i in self.regions]
    -
    -    def get_region(self, name):
    -        """
    -        Returns specific region
    -
    -        e.g
    -            dg.get_region(name = 'nyc1')  -> <Region: nyc1 New York 1>
    -
    -        Args
    -            name (str) : name of the required region
    -        Returns
    -            Region : the region with the name specified
    -        """
    -        for item in self.regions:
    -            if name == item.slug:
    -                return item
    -            if name == item.name:
    -                return item
    -        raise j.exceptions.Base("did not find region:%s" % name)
    -
    -    # SSHkeys
    -    @property
    -    def sshkeys(self):
    -        """
    -        Return list of sshkeys on digital ocean
    -
    -        e.g
    -            dg.sshkeys  -> [<SSHKey: 25882170 3bot_container_sandbox>,
    -                             <SSHKey: 27130645 Geert-root>,...]
    -
    -        Returns
    -            list : list of sshkeys
    -        """
    -
    -        return self.client.get_all_sshkeys()
    -
    -    def get_default_sshkey(self):
    -        """
    -        Return sshkey you have added to your Digital Ocean client
    -
    -        e.g
    -            dg.get_default_sshkey()  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -        Returns
    -            list : list of sshkeys
    -        """
    -        pubkeyonly = self.sshkey.public_key
    -        for item in self.sshkeys:
    -            if item.public_key.find(pubkeyonly) != -1:
    -                return item
    -        return None
    -
    -    def set_default_sshkey(self, default_sshkey):
    -        """
    -        Set sshkey you  Digital Ocean client
    -
    -        e.g
    -            dg.set_default_sshkey(ssh)  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -        Args
    -            default_sshkey (SSHKeyClient) : sshkey client you have created
    -        """
    -        self.sshkey = default_sshkey
    -
    -    def get_sshkey(self, name):
    -        """
    -        get sshkey from  Digital Ocean
    -
    -        e.g
    -            dg.get_sshkey("rafy@rafy-Inspiron-3576")   ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -        Args
    -            name (string) : sshkey name
    -
    -        Returns
    -            SSHKey : return the specified sshkey
    -        """
    -        for item in self.sshkeys:
    -            if name == item.name:
    -                return item
    -        raise j.exceptions.Base("did not find key:%s" % name)
    -
    -    def __str__(self):
    -        return "digital ocean client:%s" % self.name
    -
    -    __repr__ = __str__
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var account_images
    -
    -

    Return a list of digital ocean images and the images you have created

    -

    e.g -dg.account_images --> [, -,…]

    -

    Returns -List : list of images on digital ocean images and the images you have created

    -
    -Source code -
    @property
    -def account_images(self):
    -    """Return a list of digital ocean images and the images you have created
    -
    -    e.g
    -        dg.account_images  -> [<Image: 31354013 CentOS 6.9 x32>,
    -                                         <Image: 34902021 CentOS 6.9 x64>,...]
    -
    -    Returns
    -        List : list of images on digital ocean images and the images you have created
    -    """
    -
    -    return self.images + self.myimages
    -
    -
    -
    var client
    -
    -

    Return a new client if it is not set or it will return the already existed one

    -

    e.g -dg.client --> -Returns -Manager: client form digital ocean manager

    -
    -Source code -
    @property
    -def client(self):
    -    """Return a new client if it is not set or it will return the already existed one
    -
    -    e.g
    -        dg.client  -> <Manager>
    -    Returns
    -        Manager: client form digital ocean manager
    -    """
    -
    -    if not self._client:
    -        self._client = digitalocean.Manager(token=self.token_)
    -    return self._client
    -
    -
    -
    var droplets
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var images
    -
    -

    Return a list of digital ocean availabe images

    -

    e.g -dg.images --> [, -,…]

    -

    Returns -List : list of images on digital ocean available

    -
    -Source code -
    @property
    -def images(self):
    -    """Return a list of digital ocean availabe images
    -
    -    e.g
    -        dg.images  -> [<Image: 31354013 CentOS 6.9 x32>,
    -                                     <Image: 34902021 CentOS 6.9 x64>,...]
    -
    -    Returns
    -        List : list of images on digital ocean available
    -    """
    -    return self.client.get_distro_images()
    -
    -
    -
    var myimages
    -
    -

    Return a list of digital ocean images, you have created

    -

    e.g -dg.myimages --> [, -,…]

    -

    Returns -List : list of images on digital ocean, you have created

    -
    -Source code -
    @property
    -def myimages(self):
    -    """Return a list of digital ocean images, you have created
    -
    -    e.g
    -        dg.myimages  -> [<Image: 48614453 Unknown Zero_OS>,
    -                                    <Image: 50898718 Ubuntu JumpScale>,...]
    -
    -    Returns
    -        List : list of images on digital ocean, you have created
    -    """
    -    return self.client.get_images(private=True)
    -
    -
    -
    var name
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var projects
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var region_names
    -
    -

    Returns Digital Ocean regions

    -

    e.g -dg.region_names --> ['nyc1', 'sgp1', 'lon1', 'nyc3', 'ams3', 'fra1', 'tor1', 'sfo2', 'blr1']

    -

    Returns -list : list of digital ocean regions

    -
    -Source code -
    @property
    -def region_names(self):
    -    """Returns Digital Ocean regions
    -
    -    e.g
    -        dg.region_names  -> ['nyc1', 'sgp1', 'lon1', 'nyc3', 'ams3', 'fra1', 'tor1', 'sfo2', 'blr1']
    -
    -    Returns
    -        list : list of digital ocean regions
    -    """
    -    return [i.slug for i in self.regions]
    -
    -
    -
    var regions
    -
    -

    Return a list regions available on digital ocean

    -

    e.g -dg.regions --> [, ,…]

    -

    Returns -List : list of regions

    -
    -Source code -
    @property
    -def regions(self):
    -    """Return a list regions available on digital ocean
    -
    -    e.g
    -        dg.regions  -> [<Region: nyc1 New York 1>, <Region: sgp1 Singapore 1>,...]
    -
    -    Returns
    -        List : list of regions
    -    """
    -    return self.client.get_all_regions()
    -
    -
    -
    var sizes
    -
    -

    Return a list sizes available on digital ocean

    -

    e.g -dg.sizes -> [s-1vcpu-1gb, 512mb, s-1vcpu-2gb, 1gb, s-3vcpu-1gb,.....]

    -

    Returns -List : list of sizes

    -
    -Source code -
    @property
    -def sizes(self):
    -    """Return a list sizes available on digital ocean
    -
    -    e.g
    -        dg.sizes -> [s-1vcpu-1gb, 512mb, s-1vcpu-2gb, 1gb, s-3vcpu-1gb,.....]
    -
    -    Returns
    -        List : list of sizes
    -    """
    -    return self.client.get_all_sizes()
    -
    -
    -
    var sshkeys
    -
    -

    Return list of sshkeys on digital ocean

    -

    e.g -dg.sshkeys --> [, -,…]

    -

    Returns -list : list of sshkeys

    -
    -Source code -
    @property
    -def sshkeys(self):
    -    """
    -    Return list of sshkeys on digital ocean
    -
    -    e.g
    -        dg.sshkeys  -> [<SSHKey: 25882170 3bot_container_sandbox>,
    -                         <SSHKey: 27130645 Geert-root>,...]
    -
    -    Returns
    -        list : list of sshkeys
    -    """
    -
    -    return self.client.get_all_sshkeys()
    -
    -
    -
    var token_
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def get_default_sshkey(self) -
    -
    -

    Return sshkey you have added to your Digital Ocean client

    -

    e.g -dg.get_default_sshkey() --> -

    -

    Returns -list : list of sshkeys

    -
    -Source code -
    def get_default_sshkey(self):
    -    """
    -    Return sshkey you have added to your Digital Ocean client
    -
    -    e.g
    -        dg.get_default_sshkey()  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -    Returns
    -        list : list of sshkeys
    -    """
    -    pubkeyonly = self.sshkey.public_key
    -    for item in self.sshkeys:
    -        if item.public_key.find(pubkeyonly) != -1:
    -            return item
    -    return None
    -
    -
    -
    -def get_image(self, name) -
    -
    -

    Return an image

    -

    e.g -dg.get_image(name="CentOS") -->

    -

    Args -name (str): name of the -required image -Returns -Image : list of images on digital ocean images and the images you have created

    -
    -Source code -
    def get_image(self, name):
    -    """Return an image
    -
    -    e.g
    -        dg.get_image(name="CentOS")  -> <Image: 31354013 CentOS 6.9 x32>
    -
    -    Args
    -        name (str): name of the  required image
    -    Returns
    -        Image : list of images on digital ocean images and the images you have created
    -    """
    -    for item in self.account_images:
    -        if item.description:
    -            name_do1 = item.description.lower()
    -        else:
    -            name_do1 = ""
    -        name_do2 = item.distribution + " " + item.name
    -        print(f" - {name_do1}--{name_do2}")
    -        if name_do1.lower().find(name.lower()) != -1 or name_do2.lower().find(name.lower()) != -1:
    -            return item
    -    raise j.exceptions.Base("did not find image:%s" % name)
    -
    -
    -
    -def get_image_names(self, name='') -
    -
    -

    Return all the image -or images with a specified name -e.g -dg.get_image_names() --> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602',…] -dg.get_image_names("centos") -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602']

    -

    Args -name (str): name of the -required image -Returns -Image : list of images

    -
    -Source code -
    def get_image_names(self, name=""):
    -    """ Return all the image  or images with a specified name
    -     e.g
    -        dg.get_image_names()  -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602',...]
    -        dg.get_image_names("centos") -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602']
    -
    -    Args
    -        name (str): name of the  required image
    -    Returns
    -        Image : list of images
    -    """
    -    res = []
    -    name = name.lower()
    -    for item in self.images:
    -        if item.description:
    -            name_do = item.description.lower()
    -        else:
    -            name_do = item.distribution + " " + item.name
    -        if name_do.find(name) != -1:
    -            res.append(name_do)
    -    return res
    -
    -
    -
    -def get_region(self, name) -
    -
    -

    Returns specific region

    -

    e.g -dg.get_region(name = 'nyc1') -->

    -

    Args -name (str) : name of the required region -Returns -Region : the region with the name specified

    -
    -Source code -
    def get_region(self, name):
    -    """
    -    Returns specific region
    -
    -    e.g
    -        dg.get_region(name = 'nyc1')  -> <Region: nyc1 New York 1>
    -
    -    Args
    -        name (str) : name of the required region
    -    Returns
    -        Region : the region with the name specified
    -    """
    -    for item in self.regions:
    -        if name == item.slug:
    -            return item
    -        if name == item.name:
    -            return item
    -    raise j.exceptions.Base("did not find region:%s" % name)
    -
    -
    -
    -def get_sshkey(self, name) -
    -
    -

    get sshkey from -Digital Ocean

    -

    e.g -dg.get_sshkey("rafy@rafy-Inspiron-3576") --> -

    -

    Args -name (string) : sshkey name

    -

    Returns -SSHKey : return the specified sshkey

    -
    -Source code -
    def get_sshkey(self, name):
    -    """
    -    get sshkey from  Digital Ocean
    -
    -    e.g
    -        dg.get_sshkey("rafy@rafy-Inspiron-3576")   ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -    Args
    -        name (string) : sshkey name
    -
    -    Returns
    -        SSHKey : return the specified sshkey
    -    """
    -    for item in self.sshkeys:
    -        if name == item.name:
    -            return item
    -    raise j.exceptions.Base("did not find key:%s" % name)
    -
    -
    -
    -def set_default_sshkey(self, default_sshkey) -
    -
    -

    Set sshkey you -Digital Ocean client

    -

    e.g -dg.set_default_sshkey(ssh) --> -

    -

    Args -default_sshkey (SSHKeyClient) : sshkey client you have created

    -
    -Source code -
    def set_default_sshkey(self, default_sshkey):
    -    """
    -    Set sshkey you  Digital Ocean client
    -
    -    e.g
    -        dg.set_default_sshkey(ssh)  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
    -
    -    Args
    -        default_sshkey (SSHKeyClient) : sshkey client you have created
    -    """
    -    self.sshkey = default_sshkey
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class Droplet -(*args, **kwargs) -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class Droplet(Client):
    -    do_name = fields.String()
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -
    -    def set_digital_ocean_name(self, name):
    -        """Set a name for your Droplet to be used on Digital Ocean
    -
    -        e.g
    -            droplet.set_digital_ocean_name("test-name")
    -
    -        Args:
    -            name (str): name to be used on digital ocean
    -        """
    -        self.do_name = name
    -
    -    def get_digital_ocean_name(self):
    -        """Get a name for the Droplet which is used on digital ocean
    -
    -        e.g
    -            droplet.get_digital_ocean_name()  ->  "test-name"
    -
    -        Returns
    -            str: name for the droplet which is used on digital ocean
    -        """
    -        return self.do_name
    -
    -    def deploy(
    -        self,
    -        sshkey=None,
    -        region="Amsterdam 3",
    -        image="ubuntu 18.04",
    -        size_slug="s-1vcpu-2gb",
    -        delete=True,
    -        project_name=None,
    -    ):
    -        """
    -        Deploy your Droplet to Digital Ocean
    -
    -        Args
    -            sshkey (string): sshkey name used on digital ocean (if not set it will use the default one which already loaded)
    -            region (string): region name to deploy to
    -            image (string): Image name to be used
    -            size_slug (string): size of the droplet  (s-1vcpu-2gb,s-6vcpu-16gb,gd-8vcpu-32gb)
    -            delete (bool): delete the droplet if it is already deployed on digital ocean
    -            project_name (string): project to add this droplet it. If not specified the default project will be used.
    -
    -        """
    -        project = None
    -        if project_name:
    -            project = self.parent.projects.get_project_exist_remote(self.do_name)
    -            if not project:
    -                raise j.exceptions.Input("could not find project with name:%s" % project_name)
    -
    -        # Get ssh
    -        if not sshkey:
    -            sshkey_do = self.parent.get_default_sshkey()
    -
    -            if not sshkey_do:
    -                # means we did not find the sshkey on digital ocean yet, need to create
    -                sshkey = self.parent.sshkey
    -                key = digitalocean.SSHKey(
    -                    token=self.parent.projects.parent_instance.token_, name=sshkey.name, public_key=sshkey.public_key
    -                )
    -                key.create()
    -            sshkey_do = self.parent.get_default_sshkey()
    -            assert sshkey_do
    -            sshkey = sshkey_do.name
    -
    -        if self.parent.droplets.check_droplet_exist_remote(self.do_name):
    -            dr0 = self.parent.droplets.get_droplet_exist_remote(self.do_name)
    -            if delete:
    -                dr0.destroy()
    -            else:
    -                sshcl = j.clients.sshclient.get(name="do_%s" % self.do_name, host=dr0.ip_address, sshkey=sshkey)
    -                return dr0, sshcl
    -
    -        sshkey = self.parent.droplets.parent_instance.get_sshkey(sshkey)
    -        region = self.parent.droplets.parent_instance.get_region(region)
    -        imagedo = self.parent.droplets.parent_instance.get_image(image)
    -
    -        img_slug_or_id = imagedo.slug if imagedo.slug else imagedo.id
    -
    -        droplet = digitalocean.Droplet(
    -            token=self.parent.droplets.parent_instance.token_,
    -            name=self.do_name,
    -            region=region.slug,
    -            image=img_slug_or_id,
    -            size_slug=size_slug,
    -            ssh_keys=[sshkey],
    -            backups=False,
    -        )
    -        droplet.create()
    -
    -        if project:
    -            project.assign_resources(["do:droplet:%s" % droplet.id])
    -
    -    def delete_remote(self):
    -        """Delete Droplet from digital ocean
    -
    -        e.g
    -            droplet.delete_remote()
    -
    -        """
    -        droplet = self.parent.droplets.get_droplet_exist_remote(self.do_name)
    -        droplet.destroy()
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var do_name
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def delete_remote(self) -
    -
    -

    Delete Droplet from digital ocean

    -

    e.g -droplet.delete_remote()

    -
    -Source code -
    def delete_remote(self):
    -    """Delete Droplet from digital ocean
    -
    -    e.g
    -        droplet.delete_remote()
    -
    -    """
    -    droplet = self.parent.droplets.get_droplet_exist_remote(self.do_name)
    -    droplet.destroy()
    -
    -
    -
    -def deploy(self, sshkey=None, region='Amsterdam 3', image='ubuntu 18.04', size_slug='s-1vcpu-2gb', delete=True, project_name=None) -
    -
    -

    Deploy your Droplet to Digital Ocean

    -

    Args -sshkey (string): sshkey name used on digital ocean (if not set it will use the default one which already loaded) -region (string): region name to deploy to -image (string): Image name to be used -size_slug (string): size of the droplet -(s-1vcpu-2gb,s-6vcpu-16gb,gd-8vcpu-32gb) -delete (bool): delete the droplet if it is already deployed on digital ocean -project_name (string): project to add this droplet it. If not specified the default project will be used.

    -
    -Source code -
    def deploy(
    -    self,
    -    sshkey=None,
    -    region="Amsterdam 3",
    -    image="ubuntu 18.04",
    -    size_slug="s-1vcpu-2gb",
    -    delete=True,
    -    project_name=None,
    -):
    -    """
    -    Deploy your Droplet to Digital Ocean
    -
    -    Args
    -        sshkey (string): sshkey name used on digital ocean (if not set it will use the default one which already loaded)
    -        region (string): region name to deploy to
    -        image (string): Image name to be used
    -        size_slug (string): size of the droplet  (s-1vcpu-2gb,s-6vcpu-16gb,gd-8vcpu-32gb)
    -        delete (bool): delete the droplet if it is already deployed on digital ocean
    -        project_name (string): project to add this droplet it. If not specified the default project will be used.
    -
    -    """
    -    project = None
    -    if project_name:
    -        project = self.parent.projects.get_project_exist_remote(self.do_name)
    -        if not project:
    -            raise j.exceptions.Input("could not find project with name:%s" % project_name)
    -
    -    # Get ssh
    -    if not sshkey:
    -        sshkey_do = self.parent.get_default_sshkey()
    -
    -        if not sshkey_do:
    -            # means we did not find the sshkey on digital ocean yet, need to create
    -            sshkey = self.parent.sshkey
    -            key = digitalocean.SSHKey(
    -                token=self.parent.projects.parent_instance.token_, name=sshkey.name, public_key=sshkey.public_key
    -            )
    -            key.create()
    -        sshkey_do = self.parent.get_default_sshkey()
    -        assert sshkey_do
    -        sshkey = sshkey_do.name
    -
    -    if self.parent.droplets.check_droplet_exist_remote(self.do_name):
    -        dr0 = self.parent.droplets.get_droplet_exist_remote(self.do_name)
    -        if delete:
    -            dr0.destroy()
    -        else:
    -            sshcl = j.clients.sshclient.get(name="do_%s" % self.do_name, host=dr0.ip_address, sshkey=sshkey)
    -            return dr0, sshcl
    -
    -    sshkey = self.parent.droplets.parent_instance.get_sshkey(sshkey)
    -    region = self.parent.droplets.parent_instance.get_region(region)
    -    imagedo = self.parent.droplets.parent_instance.get_image(image)
    -
    -    img_slug_or_id = imagedo.slug if imagedo.slug else imagedo.id
    -
    -    droplet = digitalocean.Droplet(
    -        token=self.parent.droplets.parent_instance.token_,
    -        name=self.do_name,
    -        region=region.slug,
    -        image=img_slug_or_id,
    -        size_slug=size_slug,
    -        ssh_keys=[sshkey],
    -        backups=False,
    -    )
    -    droplet.create()
    -
    -    if project:
    -        project.assign_resources(["do:droplet:%s" % droplet.id])
    -
    -
    -
    -def get_digital_ocean_name(self) -
    -
    -

    Get a name for the Droplet which is used on digital ocean

    -

    e.g -droplet.get_digital_ocean_name() --> -"test-name"

    -

    Returns -str: name for the droplet which is used on digital ocean

    -
    -Source code -
    def get_digital_ocean_name(self):
    -    """Get a name for the Droplet which is used on digital ocean
    -
    -    e.g
    -        droplet.get_digital_ocean_name()  ->  "test-name"
    -
    -    Returns
    -        str: name for the droplet which is used on digital ocean
    -    """
    -    return self.do_name
    -
    -
    -
    -def set_digital_ocean_name(self, name) -
    -
    -

    Set a name for your Droplet to be used on Digital Ocean

    -

    e.g -droplet.set_digital_ocean_name("test-name") -

    -

    Args

    -
    -
    name : str
    -
    name to be used on digital ocean
    -
    -
    -Source code -
    def set_digital_ocean_name(self, name):
    -    """Set a name for your Droplet to be used on Digital Ocean
    -
    -    e.g
    -        droplet.set_digital_ocean_name("test-name")
    -
    -    Args:
    -        name (str): name to be used on digital ocean
    -    """
    -    self.do_name = name
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class DropletFactory -(*args, **kwargs) -
    -
    -

    Stored factories are a custom type of Factory, which uses current configured store backend -to store all instance configurations.

    -

    get a new stored factory given the type to create and store instances for.

    -

    Any factory can have a name, parent Base instance and a parent factory.

    -

    Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

    -

    Args

    -
    -
    type_ : Base
    -
    Base class type
    -
    name_ : str, optional
    -
    factory name. Defaults to None.
    -
    parent_instance_ : Base, optional
    -
    a parent Base instance. Defaults to None.
    -
    parent_factory_ : Factory, optional
    -
    a parent Factory. Defaults to None.
    -
    -
    -Source code -
    class DropletFactory(StoredFactory):
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -
    -    def list_remote(self, project_name=None):
    -        """
    -        List all remote droplet or list droplets for a project if it is specified
    -
    -        e.g
    -            dg.droplets.list_remote()  -> list of droplets
    -            dg.droplets.list_remote("codescalers")  -> list of droplets on codescalers project
    -
    -        Args:
    -            project_name (str) : name of project on digital ocean (optional)
    -
    -        Returns
    -            list (Droplet) : list of droplets on digital ocean
    -
    -
    -        """
    -        if project_name:
    -            project = self.parent_instance.projects.get_project_exist_remote(project_name)
    -            return project.list_droplets()
    -
    -        return self.parent_instance.client.get_all_droplets()
    -
    -    def check_droplet_exist_remote(self, name):
    -        """
    -        Check droplet exists on digital ocean
    -
    -        e.g
    -            dg.droplets.check_droplet_exist_remote("3git")  -> True
    -            dg.droplets.check_droplet_exist_remote("sdfgdfed")  -> False
    -
    -        Args:
    -            name (str) : name of droplet
    -
    -        Returns
    -            bool : True if the droplet exist or False if the droplet does not exist
    -        """
    -        for droplet in self.list_remote():
    -            if droplet.name.lower() == name.lower():
    -                return True
    -        return False
    -
    -    def get_droplet_exist_remote(self, name):
    -        """
    -        Get Droplet exists from Digital Ocean
    -
    -        e.g
    -            dg.droplets.get_droplet_exist_remote("3git")
    -
    -        Args:
    -            name (str) : name of droplet
    -
    -        Returns
    -            droplet : droplet with the name specified
    -
    -        """
    -        for droplet in self.list_remote():
    -            if droplet.name.lower() == name.lower():
    -                return droplet
    -        raise j.exceptions.Input("could not find project with name:%s on you Digital Ocean account" % name)
    -
    -    def shutdown_all(self, project_name=None):
    -        """
    -        Shutdown all the droplets or droplets in specific project
    -
    -        e.g
    -            dg.droplets.shutdown_all("codescalers")
    -            dg.droplets.shutdown_all()
    -
    -        Args:
    -            name (str) : name of the project
    -
    -        """
    -        for droplet in self.list_remote(project_name):
    -            droplet.shutdown()
    -
    -    def delete_all(self, ignore=None, interactive=True, project_name=None):
    -        """
    -        Delete all the droplets or delete all the droplets in specific project
    -
    -        e.g
    -            dg.droplets.delete_all(project_name = "codescalers")
    -            dg.droplets.delete_all()
    -
    -        Args:
    -            project_name (str) : name of the project
    -            ignore (list): list of ignored droplets to prevent their deletion
    -            interactive (bool): if True the deletion will be interactive and
    -                                confirm if you want to delete but if False it
    -                                will delete directly
    -        """
    -        if not ignore:
    -            ignore = []
    -
    -        def test(ignore, name):
    -            if name.startswith("TF-"):
    -                return False
    -            for item in ignore:
    -                if name.lower().find(item.lower()) != -1:
    -                    return False
    -            return True
    -
    -        todo = []
    -        for droplet in self.list_remote(project_name):
    -            if test(ignore, droplet.name):
    -                todo.append(droplet)
    -        if todo != []:
    -            todotxt = ",".join([i.name for i in todo])
    -            if not interactive or j.tools.console.ask_yes_no("ok to delete:%s" % todotxt):
    -                for droplet in todo:
    -                    droplet.destroy()
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def check_droplet_exist_remote(self, name) -
    -
    -

    Check droplet exists on digital ocean

    -

    e.g -dg.droplets.check_droplet_exist_remote("3git") --> True -dg.droplets.check_droplet_exist_remote("sdfgdfed") --> False

    -

    Args

    -
    -
    name (str) : name of droplet
    -
    Returns
    -
    bool : True if the droplet exist or False if the droplet does not exist
    -
    -
    -Source code -
    def check_droplet_exist_remote(self, name):
    -    """
    -    Check droplet exists on digital ocean
    -
    -    e.g
    -        dg.droplets.check_droplet_exist_remote("3git")  -> True
    -        dg.droplets.check_droplet_exist_remote("sdfgdfed")  -> False
    -
    -    Args:
    -        name (str) : name of droplet
    -
    -    Returns
    -        bool : True if the droplet exist or False if the droplet does not exist
    -    """
    -    for droplet in self.list_remote():
    -        if droplet.name.lower() == name.lower():
    -            return True
    -    return False
    -
    -
    -
    -def delete_all(self, ignore=None, interactive=True, project_name=None) -
    -
    -

    Delete all the droplets or delete all the droplets in specific project -

    -

    e.g -dg.droplets.delete_all(project_name = "codescalers") -dg.droplets.delete_all()

    -

    Args

    -
    -
    project_name (str) : name of the project
    -
    ignore : list
    -
    list of ignored droplets to prevent their deletion
    -
    interactive : bool
    -
    if True the deletion will be interactive and -confirm if you want to delete but if False it -will delete directly
    -
    -
    -Source code -
    def delete_all(self, ignore=None, interactive=True, project_name=None):
    -    """
    -    Delete all the droplets or delete all the droplets in specific project
    -
    -    e.g
    -        dg.droplets.delete_all(project_name = "codescalers")
    -        dg.droplets.delete_all()
    -
    -    Args:
    -        project_name (str) : name of the project
    -        ignore (list): list of ignored droplets to prevent their deletion
    -        interactive (bool): if True the deletion will be interactive and
    -                            confirm if you want to delete but if False it
    -                            will delete directly
    -    """
    -    if not ignore:
    -        ignore = []
    -
    -    def test(ignore, name):
    -        if name.startswith("TF-"):
    -            return False
    -        for item in ignore:
    -            if name.lower().find(item.lower()) != -1:
    -                return False
    -        return True
    -
    -    todo = []
    -    for droplet in self.list_remote(project_name):
    -        if test(ignore, droplet.name):
    -            todo.append(droplet)
    -    if todo != []:
    -        todotxt = ",".join([i.name for i in todo])
    -        if not interactive or j.tools.console.ask_yes_no("ok to delete:%s" % todotxt):
    -            for droplet in todo:
    -                droplet.destroy()
    -
    -
    -
    -def get_droplet_exist_remote(self, name) -
    -
    -

    Get Droplet exists from Digital Ocean

    -

    e.g -dg.droplets.get_droplet_exist_remote("3git")

    -

    Args

    -
    -
    name (str) : name of droplet
    -
    Returns
    -
    droplet : droplet with the name specified
    -
    -
    -Source code -
    def get_droplet_exist_remote(self, name):
    -    """
    -    Get Droplet exists from Digital Ocean
    -
    -    e.g
    -        dg.droplets.get_droplet_exist_remote("3git")
    -
    -    Args:
    -        name (str) : name of droplet
    -
    -    Returns
    -        droplet : droplet with the name specified
    -
    -    """
    -    for droplet in self.list_remote():
    -        if droplet.name.lower() == name.lower():
    -            return droplet
    -    raise j.exceptions.Input("could not find project with name:%s on you Digital Ocean account" % name)
    -
    -
    -
    -def list_remote(self, project_name=None) -
    -
    -

    List all remote droplet or list droplets for a project if it is specified

    -

    e.g -dg.droplets.list_remote() --> list of droplets -dg.droplets.list_remote("codescalers") --> list of droplets on codescalers project

    -

    Args

    -
    -
    project_name (str) : name of project on digital ocean (optional)
    -
    Returns
    -
    list (Droplet) : list of droplets on digital ocean
    -
    -
    -Source code -
    def list_remote(self, project_name=None):
    -    """
    -    List all remote droplet or list droplets for a project if it is specified
    -
    -    e.g
    -        dg.droplets.list_remote()  -> list of droplets
    -        dg.droplets.list_remote("codescalers")  -> list of droplets on codescalers project
    -
    -    Args:
    -        project_name (str) : name of project on digital ocean (optional)
    -
    -    Returns
    -        list (Droplet) : list of droplets on digital ocean
    -
    -
    -    """
    -    if project_name:
    -        project = self.parent_instance.projects.get_project_exist_remote(project_name)
    -        return project.list_droplets()
    -
    -    return self.parent_instance.client.get_all_droplets()
    -
    -
    -
    -def shutdown_all(self, project_name=None) -
    -
    -

    Shutdown all the droplets or droplets in specific project -

    -

    e.g -dg.droplets.shutdown_all("codescalers") -dg.droplets.shutdown_all()

    -

    Args

    -

    name (str) : name of the project

    -
    -Source code -
    def shutdown_all(self, project_name=None):
    -    """
    -    Shutdown all the droplets or droplets in specific project
    -
    -    e.g
    -        dg.droplets.shutdown_all("codescalers")
    -        dg.droplets.shutdown_all()
    -
    -    Args:
    -        name (str) : name of the project
    -
    -    """
    -    for droplet in self.list_remote(project_name):
    -        droplet.shutdown()
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class Project -(*args, **kwargs) -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class Project(Client):
    -    do_name = fields.String()
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -
    -    def set_digital_ocean_name(self, name):
    -        """Set a name for your project to be used on Digital Ocean
    -        e.g
    -            project.set_digital_ocean_name("test project DO client")
    -
    -        Args:
    -            name (str): name to be used on digital ocean
    -        """
    -        self.do_name = name
    -
    -    def get_digital_ocean_name(self):
    -        """Get a name for the project which is used on digital ocean
    -        e.g
    -            project.get_digital_ocean_name()  ->  "test project DO client"
    -
    -        Returns:
    -            str: name for the project which is used on digital ocean
    -        """
    -        return self.do_name
    -
    -    def deploy(self, purpose, description="", environment="", is_default=False):
    -        """Create a digital ocean project
    -        e.g
    -            project.deploy(purpose="testing digital ocean client")  -> project
    -        Args:
    -            purpose(str): purpose of the project (not optional)
    -            description(str): description of the project, defaults to ""
    -            environment(str): environment of project's resources, defaults to ""
    -            is_default(bool): make this the default project for your user
    -
    -        Returns:
    -            project: The project object that has been created
    -        """
    -
    -        if self.parent.projects.check_project_exist_remote(self.do_name):
    -            raise j.exceptions.Value("A project with the same name already exists")
    -
    -        project = ProjectManagement(
    -            token=self.parent.projects.parent_instance.token_,
    -            name=self.do_name,
    -            purpose=purpose,
    -            description=description,
    -            environment=environment,
    -            is_default=is_default,
    -        )
    -        project.create()
    -
    -        if is_default:
    -            project.update(is_default=True)
    -
    -        return project
    -
    -    def delete_remote(self):
    -        """Delete the project from Digital Ocean (A project can't be deleted unless it has no resources.)
    -
    -        e.g
    -            project.delete_remote()
    -        """
    -        project = self.parent.projects.get_project_exist_remote(self.do_name)
    -        project.delete()
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var do_name
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def delete_remote(self) -
    -
    -

    Delete the project from Digital Ocean (A project can't be deleted unless it has no resources.)

    -

    e.g -project.delete_remote()

    -
    -Source code -
    def delete_remote(self):
    -    """Delete the project from Digital Ocean (A project can't be deleted unless it has no resources.)
    -
    -    e.g
    -        project.delete_remote()
    -    """
    -    project = self.parent.projects.get_project_exist_remote(self.do_name)
    -    project.delete()
    -
    -
    -
    -def deploy(self, purpose, description='', environment='', is_default=False) -
    -
    -

    Create a digital ocean project -e.g -project.deploy(purpose="testing digital ocean client") --> project

    -

    Args

    -

    purpose(str): purpose of the project (not optional) -description(str): description of the project, defaults to "" -environment(str): environment of project's resources, defaults to "" -is_default(bool): make this the default project for your user

    -

    Returns

    -
    -
    project
    -
    The project object that has been created
    -
    -
    -Source code -
    def deploy(self, purpose, description="", environment="", is_default=False):
    -    """Create a digital ocean project
    -    e.g
    -        project.deploy(purpose="testing digital ocean client")  -> project
    -    Args:
    -        purpose(str): purpose of the project (not optional)
    -        description(str): description of the project, defaults to ""
    -        environment(str): environment of project's resources, defaults to ""
    -        is_default(bool): make this the default project for your user
    -
    -    Returns:
    -        project: The project object that has been created
    -    """
    -
    -    if self.parent.projects.check_project_exist_remote(self.do_name):
    -        raise j.exceptions.Value("A project with the same name already exists")
    -
    -    project = ProjectManagement(
    -        token=self.parent.projects.parent_instance.token_,
    -        name=self.do_name,
    -        purpose=purpose,
    -        description=description,
    -        environment=environment,
    -        is_default=is_default,
    -    )
    -    project.create()
    -
    -    if is_default:
    -        project.update(is_default=True)
    -
    -    return project
    -
    -
    -
    -def get_digital_ocean_name(self) -
    -
    -

    Get a name for the project which is used on digital ocean -e.g -project.get_digital_ocean_name() --> -"test project DO client"

    -

    Returns

    -
    -
    str
    -
    name for the project which is used on digital ocean
    -
    -
    -Source code -
    def get_digital_ocean_name(self):
    -    """Get a name for the project which is used on digital ocean
    -    e.g
    -        project.get_digital_ocean_name()  ->  "test project DO client"
    -
    -    Returns:
    -        str: name for the project which is used on digital ocean
    -    """
    -    return self.do_name
    -
    -
    -
    -def set_digital_ocean_name(self, name) -
    -
    -

    Set a name for your project to be used on Digital Ocean -e.g -project.set_digital_ocean_name("test project DO client")

    -

    Args

    -
    -
    name : str
    -
    name to be used on digital ocean
    -
    -
    -Source code -
    def set_digital_ocean_name(self, name):
    -    """Set a name for your project to be used on Digital Ocean
    -    e.g
    -        project.set_digital_ocean_name("test project DO client")
    -
    -    Args:
    -        name (str): name to be used on digital ocean
    -    """
    -    self.do_name = name
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class ProjectFactory -(*args, **kwargs) -
    -
    -

    Stored factories are a custom type of Factory, which uses current configured store backend -to store all instance configurations.

    -

    get a new stored factory given the type to create and store instances for.

    -

    Any factory can have a name, parent Base instance and a parent factory.

    -

    Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

    -

    Args

    -
    -
    type_ : Base
    -
    Base class type
    -
    name_ : str, optional
    -
    factory name. Defaults to None.
    -
    parent_instance_ : Base, optional
    -
    a parent Base instance. Defaults to None.
    -
    parent_factory_ : Factory, optional
    -
    a parent Factory. Defaults to None.
    -
    -
    -Source code -
    class ProjectFactory(StoredFactory):
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -
    -    def list_remote(self):
    -        """
    -        Returns list of projects on Digital Ocean
    -
    -        e.g
    -            dg.projects.list_remote()  -> list of projects
    -
    -        Returns
    -            list(projects): list of projects on digital ocean
    -
    -        """
    -        return ProjectManagement.list(self.parent_instance.client)
    -
    -    def check_project_exist_remote(self, name):
    -        """
    -        Check a project with specific name exits on Digital Ocean
    -
    -        e.g
    -            dg.projects.check_project_exist_remote("codescalers")  -> True
    -            dg.projects.check_project_exist_remote("dsrfjsdfjl")  -> False
    -
    -        Args
    -            name (str): name of the project
    -
    -        Returns
    -            bool : True if the project exits and False if the project does not exist on digital ocean
    -        """
    -        for project in self.list_remote():
    -            if project.name == name:
    -                return True
    -        return False
    -
    -    def get_project_exist_remote(self, name):
    -        """
    -        Get a project with specifc name from  Digital Ocean.
    -
    -        e.g
    -            dg.projects.get_project_exist_remote("codescalers")  -> project
    -
    -        Args
    -            name (str): name of the project
    -
    -        Returns
    -            Project : a project from digital ocean with the name specified
    -        """
    -        for project in self.list_remote():
    -            if project.name == name:
    -                return project
    -        raise j.exceptions.Input("could not find project with name:%s on you Digital Ocean account" % name)
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def check_project_exist_remote(self, name) -
    -
    -

    Check a project with specific name exits on Digital Ocean

    -

    e.g -dg.projects.check_project_exist_remote("codescalers") --> True -dg.projects.check_project_exist_remote("dsrfjsdfjl") --> False

    -

    Args -name (str): name of the project

    -

    Returns -bool : True if the project exits and False if the project does not exist on digital ocean

    -
    -Source code -
    def check_project_exist_remote(self, name):
    -    """
    -    Check a project with specific name exits on Digital Ocean
    -
    -    e.g
    -        dg.projects.check_project_exist_remote("codescalers")  -> True
    -        dg.projects.check_project_exist_remote("dsrfjsdfjl")  -> False
    -
    -    Args
    -        name (str): name of the project
    -
    -    Returns
    -        bool : True if the project exits and False if the project does not exist on digital ocean
    -    """
    -    for project in self.list_remote():
    -        if project.name == name:
    -            return True
    -    return False
    -
    -
    -
    -def get_project_exist_remote(self, name) -
    -
    -

    Get a project with specifc name from -Digital Ocean.

    -

    e.g -dg.projects.get_project_exist_remote("codescalers") --> project

    -

    Args -name (str): name of the project

    -

    Returns -Project : a project from digital ocean with the name specified

    -
    -Source code -
    def get_project_exist_remote(self, name):
    -    """
    -    Get a project with specifc name from  Digital Ocean.
    -
    -    e.g
    -        dg.projects.get_project_exist_remote("codescalers")  -> project
    -
    -    Args
    -        name (str): name of the project
    -
    -    Returns
    -        Project : a project from digital ocean with the name specified
    -    """
    -    for project in self.list_remote():
    -        if project.name == name:
    -            return project
    -    raise j.exceptions.Input("could not find project with name:%s on you Digital Ocean account" % name)
    -
    -
    -
    -def list_remote(self) -
    -
    -

    Returns list of projects on Digital Ocean

    -

    e.g -dg.projects.list_remote() --> list of projects

    -

    Returns -list(projects): list of projects on digital ocean

    -
    -Source code -
    def list_remote(self):
    -    """
    -    Returns list of projects on Digital Ocean
    -
    -    e.g
    -        dg.projects.list_remote()  -> list of projects
    -
    -    Returns
    -        list(projects): list of projects on digital ocean
    -
    -    """
    -    return ProjectManagement.list(self.parent_instance.client)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/digitalocean/index.html b/docs/api/jumpscale/clients/digitalocean/index.html deleted file mode 100644 index a01704759..000000000 --- a/docs/api/jumpscale/clients/digitalocean/index.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - -jumpscale.clients.digitalocean API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.digitalocean

    -
    -
    -
    -Source code -
    def export_module_as():
    -
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .digitalocean import DigitalOcean
    -
    -    return StoredFactory(DigitalOcean)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.digitalocean.digitalocean
    -
    -

    This module is used to manage your digital ocean account, create droplet,list all the droplets, destroy droplets, create project, list all the …

    -
    -
    jumpscale.clients.digitalocean.project
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .digitalocean import DigitalOcean
    -
    -    return StoredFactory(DigitalOcean)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/digitalocean/project.html b/docs/api/jumpscale/clients/digitalocean/project.html deleted file mode 100644 index 7c3090bdb..000000000 --- a/docs/api/jumpscale/clients/digitalocean/project.html +++ /dev/null @@ -1,665 +0,0 @@ - - - - - - -jumpscale.clients.digitalocean.project API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.digitalocean.project

    -
    -
    -
    -Source code -
    from digitalocean import Droplet
    -from digitalocean.baseapi import BaseAPI, Error, GET, POST, DELETE, PUT
    -
    -
    -class ProjectManagement(BaseAPI):
    -    """Project management
    -
    -    Attributes accepted at creation time:
    -
    -    Args:
    -        name (str): project name
    -        description (str): project size
    -        purpose (str): purpose of the project
    -        environemnt (str): environment of the project's resources
    -
    -    Attributes returned by API:
    -        * id (int): project id
    -        * owner_uuid (str): uuid of the project owner
    -        * owner_id (str): id of the project owner
    -        * name (str): project name
    -        * description (str): project description
    -        * purpose (str): project purpose
    -        * environment (str): environment of the project's resources
    -        * is_default (bool): If true, all resources will be added to this project if no project is specified.
    -        * created_at (str): creation date in format u'2014-11-06T10:42:09Z'
    -        * updated_at (str): update date in format u'2014-11-06T10:42:09Z'
    -
    -    """
    -
    -    def __init__(self, *args, **kwargs):
    -        # Defining default values
    -        self.id = None
    -        self.name = None
    -        self.owner_uuid = None
    -        self.owner_id = None
    -        self.description = None
    -        self.purpose = None
    -        self.environment = None
    -        self.is_default = False
    -        self.updated_at = None
    -        self.created_at = None
    -
    -        # This will load also the values passed
    -        super(ProjectManagement, self).__init__(*args, **kwargs)
    -
    -    @classmethod
    -    def get_object(cls, api_token, project_id):
    -        """Class method that will return a Project object by ID.
    -
    -        Args:
    -            api_token (str): token
    -            project_id (int): project id
    -        """
    -        project = cls(token=api_token, id=project_id)
    -        project.load()
    -        return project
    -
    -    @classmethod
    -    def list(cls, client):
    -
    -        data = client.get_data("projects")
    -
    -        projects = list()
    -        for jsoned in data["projects"]:
    -            project = cls(**jsoned)
    -            project.token = client.token
    -
    -            projects.append(project)
    -
    -        return projects
    -
    -    def load(self):
    -        """
    -        Fetch data about project - use this instead of get_data()
    -        """
    -        projects = self.get_data("projects/%s" % self.id)
    -        project = projects["project"]
    -
    -        for attr in project.keys():
    -            setattr(self, attr, project[attr])
    -
    -        return self
    -
    -    def _update_data(self, project):
    -        self.id = project["id"]
    -        self.owner_uuid = project["owner_uuid"]
    -        self.owner_id = project["owner_id"]
    -        self.name = project["name"]
    -        self.description = project["description"]
    -        self.purpose = project["purpose"]
    -        self.environment = project["environment"]
    -        self.is_default = project["is_default"]
    -        self.created_at = project["created_at"]
    -        self.updated_at = project["updated_at"]
    -
    -    def create(self, *args, **kwargs):
    -        """
    -        Create the project with object properties.
    -
    -        Note: Every argument and parameter given to this method will be
    -        assigned to the object.
    -        """
    -        for attr in kwargs.keys():
    -            setattr(self, attr, kwargs[attr])
    -
    -        data = {
    -            "name": self.name,
    -            "description": self.description,
    -            "purpose": self.purpose,
    -            "environment": self.environment,
    -        }
    -
    -        data = self.get_data("projects", type=POST, params=data)
    -        self._update_data(data["project"])
    -
    -    def update(self, *args, **kwargs):
    -        """
    -        Update the project with object properties.
    -
    -        Note: Every argument and parameter given to this method will be
    -        assigned to the object.
    -        """
    -        for attr in kwargs.keys():
    -            setattr(self, attr, kwargs[attr])
    -
    -        data = {
    -            "name": self.name,
    -            "description": self.description,
    -            "purpose": self.purpose,
    -            "environment": self.environment,
    -            "is_default": self.is_default,
    -        }
    -
    -        data = self.get_data("projects/%s" % self.id, type=PUT, params=data)
    -        self._update_data(data["project"])
    -
    -    def delete(self):
    -        """
    -        Delete the project.
    -        To be deleted, a project must not have any resources assigned to it. Any existing resources must first be reassigned or destroyed.
    -        """
    -        self.get_data("projects/%s" % self.id, type=DELETE)
    -
    -    def list_resources(self):
    -        """
    -        List all resources in the project
    -        """
    -        return self.get_data("projects/%s/resources" % self.id)["resources"]
    -
    -    def list_droplets(self):
    -        """
    -        List all droplets in the project
    -        """
    -        resources = self.list_resources()
    -        droplets = []
    -        for resource in resources:
    -            if not resource["urn"].startswith("do:droplet:"):
    -                continue
    -            droplet_id = resource["urn"].replace("do:droplet:", "")
    -            droplet = Droplet.get_object(api_token=self.token, droplet_id=droplet_id)
    -            droplets.append(droplet)
    -
    -        return droplets
    -
    -    def assign_resources(self, resources):
    -        """Assign resources to the project.
    -
    -        :param resources: A list of uniform resource names (URNs) to be added to a project.
    -        :type resources: [str]
    -        """
    -        self.get_data("projects/%s/resources" % self.id, type=POST, params={"resources": resources})
    -
    -    def __str__(self):
    -        return "<Project: %s %s>" % (self.id, self.name)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class ProjectManagement -(*args, **kwargs) -
    -
    -

    Project management

    -

    Attributes accepted at creation time:

    -

    Args

    -
    -
    name : str
    -
    project name
    -
    description : str
    -
    project size
    -
    purpose : str
    -
    purpose of the project
    -
    environemnt : str
    -
    environment of the project's resources
    -
    -

    Attributes returned by API: -* id (int): project id -* owner_uuid (str): uuid of the project owner -* owner_id (str): id of the project owner -* name (str): project name -* description (str): project description -* purpose (str): project purpose -* environment (str): environment of the project's resources -* is_default (bool): If true, all resources will be added to this project if no project is specified. -* created_at (str): creation date in format u'2014-11-06T10:42:09Z' -* updated_at (str): update date in format u'2014-11-06T10:42:09Z'

    -
    -Source code -
    class ProjectManagement(BaseAPI):
    -    """Project management
    -
    -    Attributes accepted at creation time:
    -
    -    Args:
    -        name (str): project name
    -        description (str): project size
    -        purpose (str): purpose of the project
    -        environemnt (str): environment of the project's resources
    -
    -    Attributes returned by API:
    -        * id (int): project id
    -        * owner_uuid (str): uuid of the project owner
    -        * owner_id (str): id of the project owner
    -        * name (str): project name
    -        * description (str): project description
    -        * purpose (str): project purpose
    -        * environment (str): environment of the project's resources
    -        * is_default (bool): If true, all resources will be added to this project if no project is specified.
    -        * created_at (str): creation date in format u'2014-11-06T10:42:09Z'
    -        * updated_at (str): update date in format u'2014-11-06T10:42:09Z'
    -
    -    """
    -
    -    def __init__(self, *args, **kwargs):
    -        # Defining default values
    -        self.id = None
    -        self.name = None
    -        self.owner_uuid = None
    -        self.owner_id = None
    -        self.description = None
    -        self.purpose = None
    -        self.environment = None
    -        self.is_default = False
    -        self.updated_at = None
    -        self.created_at = None
    -
    -        # This will load also the values passed
    -        super(ProjectManagement, self).__init__(*args, **kwargs)
    -
    -    @classmethod
    -    def get_object(cls, api_token, project_id):
    -        """Class method that will return a Project object by ID.
    -
    -        Args:
    -            api_token (str): token
    -            project_id (int): project id
    -        """
    -        project = cls(token=api_token, id=project_id)
    -        project.load()
    -        return project
    -
    -    @classmethod
    -    def list(cls, client):
    -
    -        data = client.get_data("projects")
    -
    -        projects = list()
    -        for jsoned in data["projects"]:
    -            project = cls(**jsoned)
    -            project.token = client.token
    -
    -            projects.append(project)
    -
    -        return projects
    -
    -    def load(self):
    -        """
    -        Fetch data about project - use this instead of get_data()
    -        """
    -        projects = self.get_data("projects/%s" % self.id)
    -        project = projects["project"]
    -
    -        for attr in project.keys():
    -            setattr(self, attr, project[attr])
    -
    -        return self
    -
    -    def _update_data(self, project):
    -        self.id = project["id"]
    -        self.owner_uuid = project["owner_uuid"]
    -        self.owner_id = project["owner_id"]
    -        self.name = project["name"]
    -        self.description = project["description"]
    -        self.purpose = project["purpose"]
    -        self.environment = project["environment"]
    -        self.is_default = project["is_default"]
    -        self.created_at = project["created_at"]
    -        self.updated_at = project["updated_at"]
    -
    -    def create(self, *args, **kwargs):
    -        """
    -        Create the project with object properties.
    -
    -        Note: Every argument and parameter given to this method will be
    -        assigned to the object.
    -        """
    -        for attr in kwargs.keys():
    -            setattr(self, attr, kwargs[attr])
    -
    -        data = {
    -            "name": self.name,
    -            "description": self.description,
    -            "purpose": self.purpose,
    -            "environment": self.environment,
    -        }
    -
    -        data = self.get_data("projects", type=POST, params=data)
    -        self._update_data(data["project"])
    -
    -    def update(self, *args, **kwargs):
    -        """
    -        Update the project with object properties.
    -
    -        Note: Every argument and parameter given to this method will be
    -        assigned to the object.
    -        """
    -        for attr in kwargs.keys():
    -            setattr(self, attr, kwargs[attr])
    -
    -        data = {
    -            "name": self.name,
    -            "description": self.description,
    -            "purpose": self.purpose,
    -            "environment": self.environment,
    -            "is_default": self.is_default,
    -        }
    -
    -        data = self.get_data("projects/%s" % self.id, type=PUT, params=data)
    -        self._update_data(data["project"])
    -
    -    def delete(self):
    -        """
    -        Delete the project.
    -        To be deleted, a project must not have any resources assigned to it. Any existing resources must first be reassigned or destroyed.
    -        """
    -        self.get_data("projects/%s" % self.id, type=DELETE)
    -
    -    def list_resources(self):
    -        """
    -        List all resources in the project
    -        """
    -        return self.get_data("projects/%s/resources" % self.id)["resources"]
    -
    -    def list_droplets(self):
    -        """
    -        List all droplets in the project
    -        """
    -        resources = self.list_resources()
    -        droplets = []
    -        for resource in resources:
    -            if not resource["urn"].startswith("do:droplet:"):
    -                continue
    -            droplet_id = resource["urn"].replace("do:droplet:", "")
    -            droplet = Droplet.get_object(api_token=self.token, droplet_id=droplet_id)
    -            droplets.append(droplet)
    -
    -        return droplets
    -
    -    def assign_resources(self, resources):
    -        """Assign resources to the project.
    -
    -        :param resources: A list of uniform resource names (URNs) to be added to a project.
    -        :type resources: [str]
    -        """
    -        self.get_data("projects/%s/resources" % self.id, type=POST, params={"resources": resources})
    -
    -    def __str__(self):
    -        return "<Project: %s %s>" % (self.id, self.name)
    -
    -

    Ancestors

    -
      -
    • digitalocean.BaseAPI
    • -
    -

    Static methods

    -
    -
    -def get_object(api_token, project_id) -
    -
    -

    Class method that will return a Project object by ID.

    -

    Args

    -
    -
    api_token : str
    -
    token
    -
    project_id : int
    -
    project id
    -
    -
    -Source code -
    @classmethod
    -def get_object(cls, api_token, project_id):
    -    """Class method that will return a Project object by ID.
    -
    -    Args:
    -        api_token (str): token
    -        project_id (int): project id
    -    """
    -    project = cls(token=api_token, id=project_id)
    -    project.load()
    -    return project
    -
    -
    -
    -def list(client) -
    -
    -
    -
    -Source code -
    @classmethod
    -def list(cls, client):
    -
    -    data = client.get_data("projects")
    -
    -    projects = list()
    -    for jsoned in data["projects"]:
    -        project = cls(**jsoned)
    -        project.token = client.token
    -
    -        projects.append(project)
    -
    -    return projects
    -
    -
    -
    -

    Methods

    -
    -
    -def assign_resources(self, resources) -
    -
    -

    Assign resources to the project.

    -

    :param resources: A list of uniform resource names (URNs) to be added to a project. -:type resources: [str]

    -
    -Source code -
    def assign_resources(self, resources):
    -    """Assign resources to the project.
    -
    -    :param resources: A list of uniform resource names (URNs) to be added to a project.
    -    :type resources: [str]
    -    """
    -    self.get_data("projects/%s/resources" % self.id, type=POST, params={"resources": resources})
    -
    -
    -
    -def create(self, *args, **kwargs) -
    -
    -

    Create the project with object properties.

    -

    Note: Every argument and parameter given to this method will be -assigned to the object.

    -
    -Source code -
    def create(self, *args, **kwargs):
    -    """
    -    Create the project with object properties.
    -
    -    Note: Every argument and parameter given to this method will be
    -    assigned to the object.
    -    """
    -    for attr in kwargs.keys():
    -        setattr(self, attr, kwargs[attr])
    -
    -    data = {
    -        "name": self.name,
    -        "description": self.description,
    -        "purpose": self.purpose,
    -        "environment": self.environment,
    -    }
    -
    -    data = self.get_data("projects", type=POST, params=data)
    -    self._update_data(data["project"])
    -
    -
    -
    -def delete(self) -
    -
    -

    Delete the project. -To be deleted, a project must not have any resources assigned to it. Any existing resources must first be reassigned or destroyed.

    -
    -Source code -
    def delete(self):
    -    """
    -    Delete the project.
    -    To be deleted, a project must not have any resources assigned to it. Any existing resources must first be reassigned or destroyed.
    -    """
    -    self.get_data("projects/%s" % self.id, type=DELETE)
    -
    -
    -
    -def list_droplets(self) -
    -
    -

    List all droplets in the project

    -
    -Source code -
    def list_droplets(self):
    -    """
    -    List all droplets in the project
    -    """
    -    resources = self.list_resources()
    -    droplets = []
    -    for resource in resources:
    -        if not resource["urn"].startswith("do:droplet:"):
    -            continue
    -        droplet_id = resource["urn"].replace("do:droplet:", "")
    -        droplet = Droplet.get_object(api_token=self.token, droplet_id=droplet_id)
    -        droplets.append(droplet)
    -
    -    return droplets
    -
    -
    -
    -def list_resources(self) -
    -
    -

    List all resources in the project

    -
    -Source code -
    def list_resources(self):
    -    """
    -    List all resources in the project
    -    """
    -    return self.get_data("projects/%s/resources" % self.id)["resources"]
    -
    -
    -
    -def load(self) -
    -
    -

    Fetch data about project - use this instead of get_data()

    -
    -Source code -
    def load(self):
    -    """
    -    Fetch data about project - use this instead of get_data()
    -    """
    -    projects = self.get_data("projects/%s" % self.id)
    -    project = projects["project"]
    -
    -    for attr in project.keys():
    -        setattr(self, attr, project[attr])
    -
    -    return self
    -
    -
    -
    -def update(self, *args, **kwargs) -
    -
    -

    Update the project with object properties.

    -

    Note: Every argument and parameter given to this method will be -assigned to the object.

    -
    -Source code -
    def update(self, *args, **kwargs):
    -    """
    -    Update the project with object properties.
    -
    -    Note: Every argument and parameter given to this method will be
    -    assigned to the object.
    -    """
    -    for attr in kwargs.keys():
    -        setattr(self, attr, kwargs[attr])
    -
    -    data = {
    -        "name": self.name,
    -        "description": self.description,
    -        "purpose": self.purpose,
    -        "environment": self.environment,
    -        "is_default": self.is_default,
    -    }
    -
    -    data = self.get_data("projects/%s" % self.id, type=PUT, params=data)
    -    self._update_data(data["project"])
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/clients/gdrive/gdrive.html b/docs/api/jumpscale/clients/gdrive/gdrive.html deleted file mode 100644 index d5f84cac2..000000000 --- a/docs/api/jumpscale/clients/gdrive/gdrive.html +++ /dev/null @@ -1,308 +0,0 @@ - - - - - - -jumpscale.clients.gdrive.gdrive API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.gdrive.gdrive

    -
    -
    -
    -Source code -
    from google.oauth2 import service_account
    -from googleapiclient.discovery import build
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -
    -
    -"""
    -JS-NG> cl = j.clients.gdrive.new("name")
    -JS-NG> cl.credfile = "/Downloads/pelagic-core-251306-8b2323198535.json"
    -JS-NG> cl.get_file('1kUHHSjtPNUN2dAJQSAeCWMvrZzUO2YNW')
    -{'kind': 'drive#file', 'id': '1kUHHSjtPNUN2dAJQSAeCWMvrZzUO2YNW', 'name': 'my.txt', 'mimeType': 'text/plain'}
    -
    -cl.files.get_media(fileId='1kUHHSjtPNUN2dAJQSAeCWMvrZzUO2YNW')
    -
    -JS-NG> cl.get_service('drive', 'v3')
    -<googleapiclient.discovery.Resource object at 0x7f3fa8982240>
    -"""
    -
    -
    -SCOPES = [
    -    "https://www.googleapis.com/auth/drive",
    -    "https://www.googleapis.com/auth/drive.file",
    -    "https://www.googleapis.com/auth/drive.appdata",
    -    "https://www.googleapis.com/auth/drive.scripts",
    -    "https://www.googleapis.com/auth/drive.metadata",
    -]
    -
    -DRIVE_BUILD_VERSION = "v3"
    -
    -
    -class GdriveClient(Client):
    -    credfile = fields.String()
    -
    -    def __init__(self):
    -        self.__credentials = None
    -        self.__files = None
    -        super().__init__()
    -
    -    @property
    -    def credentials(self):
    -        if not self.__credentials:
    -            self.__credentials = service_account.Credentials.from_service_account_file(
    -                self.credfile, scopes=SCOPES
    -            )
    -        return self.__credentials
    -
    -    @property
    -    def files(self):
    -        if not self.__files:
    -            drive = self.get_service("drive", DRIVE_BUILD_VERSION)
    -            self.__files = drive.files()
    -        return self.__files
    -
    -    def get_service(self, name, version):
    -        return build(name, version, credentials=self.credentials)
    -
    -    def get_file(self, file_id):
    -        return self.files.get(fileId=file_id).execute()
    -
    -    def export_pdf(self, file_id, export_path):
    -        response = self.files.export_media(fileId=file_id, mimeType="application/pdf").execute()
    -        with open(export_path, "wb") as expr:
    -            expr.write(response)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class GdriveClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class GdriveClient(Client):
    -    credfile = fields.String()
    -
    -    def __init__(self):
    -        self.__credentials = None
    -        self.__files = None
    -        super().__init__()
    -
    -    @property
    -    def credentials(self):
    -        if not self.__credentials:
    -            self.__credentials = service_account.Credentials.from_service_account_file(
    -                self.credfile, scopes=SCOPES
    -            )
    -        return self.__credentials
    -
    -    @property
    -    def files(self):
    -        if not self.__files:
    -            drive = self.get_service("drive", DRIVE_BUILD_VERSION)
    -            self.__files = drive.files()
    -        return self.__files
    -
    -    def get_service(self, name, version):
    -        return build(name, version, credentials=self.credentials)
    -
    -    def get_file(self, file_id):
    -        return self.files.get(fileId=file_id).execute()
    -
    -    def export_pdf(self, file_id, export_path):
    -        response = self.files.export_media(fileId=file_id, mimeType="application/pdf").execute()
    -        with open(export_path, "wb") as expr:
    -            expr.write(response)
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var credentials
    -
    -
    -
    -Source code -
    @property
    -def credentials(self):
    -    if not self.__credentials:
    -        self.__credentials = service_account.Credentials.from_service_account_file(
    -            self.credfile, scopes=SCOPES
    -        )
    -    return self.__credentials
    -
    -
    -
    var credfile
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var files
    -
    -
    -
    -Source code -
    @property
    -def files(self):
    -    if not self.__files:
    -        drive = self.get_service("drive", DRIVE_BUILD_VERSION)
    -        self.__files = drive.files()
    -    return self.__files
    -
    -
    -
    -

    Methods

    -
    -
    -def export_pdf(self, file_id, export_path) -
    -
    -
    -
    -Source code -
    def export_pdf(self, file_id, export_path):
    -    response = self.files.export_media(fileId=file_id, mimeType="application/pdf").execute()
    -    with open(export_path, "wb") as expr:
    -        expr.write(response)
    -
    -
    -
    -def get_file(self, file_id) -
    -
    -
    -
    -Source code -
    def get_file(self, file_id):
    -    return self.files.get(fileId=file_id).execute()
    -
    -
    -
    -def get_service(self, name, version) -
    -
    -
    -
    -Source code -
    def get_service(self, name, version):
    -    return build(name, version, credentials=self.credentials)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/gdrive/index.html b/docs/api/jumpscale/clients/gdrive/index.html deleted file mode 100644 index 79e86bfac..000000000 --- a/docs/api/jumpscale/clients/gdrive/index.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -jumpscale.clients.gdrive API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.gdrive

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .gdrive import GdriveClient
    -
    -    return StoredFactory(GdriveClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.gdrive.gdrive
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .gdrive import GdriveClient
    -
    -    return StoredFactory(GdriveClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/gedis/gedis.html b/docs/api/jumpscale/clients/gedis/gedis.html deleted file mode 100644 index bbb47cac6..000000000 --- a/docs/api/jumpscale/clients/gedis/gedis.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - -jumpscale.clients.gedis.gedis API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.gedis.gedis

    -
    -
    -
    -Source code -
    import inspect
    -import json
    -import os
    -import sys
    -from functools import partial
    -
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -from jumpscale.loader import j
    -from jumpscale.servers.gedis.server import GedisErrorTypes, deserialize, serialize
    -from jumpscale.tools.codeloader import load_python_module
    -
    -
    -class ActorResult:
    -    def __init__(self, **kwargs):
    -        self.success = kwargs.get("success", True)
    -        self.result = kwargs.get("result", None)
    -        self.error = kwargs.get("error", None)
    -        self.error_type = kwargs.get("error_type", None)
    -        self.is_async = kwargs.get("is_async", False)
    -        self.task_id = kwargs.get("task_id", None)
    -
    -    def __dir__(self):
    -        return list(self.__dict__.keys())
    -
    -    def __repr__(self):
    -        return str(self.__dict__)
    -
    -
    -class ActorProxy:
    -    def __init__(self, actor_name, actor_info, client):
    -        """ActorProxy to remote actor on the server side
    -
    -        Arguments:
    -            actor_name {str} -- [description]
    -            actor_info {dict} -- actor information dict e.g { method_name: { args: [], 'doc':...} }
    -            gedis_client {GedisClient} -- gedis client reference
    -        """
    -        self.actor_name = actor_name
    -        self.actor_info = actor_info
    -        self.client = client
    -
    -    def __dir__(self):
    -        """Delegate the available functions on the ActorProxy to `actor_info` keys
    -
    -        Returns:
    -            list -- methods available on the ActorProxy
    -        """
    -        return list(self.actor_info["methods"].keys())
    -
    -    def __getattr__(self, method):
    -        """Return a function representing the remote function on the actual actor
    -
    -        Arguments:
    -            attr {str} -- method name
    -
    -        Returns:
    -            function -- function waiting on the arguments
    -        """
    -
    -        def function(*args, **kwargs):
    -            return self.client.execute(self.actor_name, method, *args, **kwargs)
    -
    -        func = partial(function)
    -        func.__doc__ = self.actor_info["methods"][method]["doc"]
    -        return func
    -
    -
    -class ActorsCollection:
    -    def __init__(self, actors):
    -        self._actors = actors
    -
    -    def __dir__(self):
    -        return list(self._actors.keys())
    -
    -    def __getattr__(self, actor_name):
    -        if actor_name in self._actors:
    -            return self._actors[actor_name]
    -
    -
    -class GedisClient(Client):
    -    name = fields.String(default="local")
    -    hostname = fields.String(default="localhost")
    -    port = fields.Integer(default=16000)
    -    raise_on_error = fields.Boolean(default=False)
    -    disable_deserialization = fields.Boolean(default=False)
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -        self._redisclient = None
    -        self._loaded_actors = {}
    -        self._loaded_modules = []
    -        self.actors = None
    -        self._load_actors()
    -
    -    @property
    -    def redis_client(self):
    -        if self._redisclient is None:
    -            self._redisclient = j.clients.redis.get(name=f"gedis_{self.name}", hostname=self.hostname, port=self.port)
    -        return self._redisclient
    -
    -    def _load_module(self, path, force_reload=False):
    -        load_python_module(path, force_reload=force_reload)
    -        if path not in self._loaded_modules:
    -            self._loaded_modules.append(path)
    -
    -    def _load_actors(self, force_reload=False):
    -        self._loaded_actors = {}
    -        for actor_name in self.list_actors():
    -            actor_info = self._get_actor_info(actor_name)
    -            self._load_module(actor_info["path"], force_reload=force_reload)
    -            self._loaded_actors[actor_name] = ActorProxy(actor_name, actor_info, self)
    -
    -        self.actors = ActorsCollection(self._loaded_actors)
    -
    -    def _get_actor_info(self, actor_name):
    -        return self.execute(actor_name, "info", die=True).result
    -
    -    def list_actors(self) -> list:
    -        """List actors
    -
    -        Returns:
    -            list -- List of loaded actors
    -        """
    -        return self.execute("core", "list_actors", die=True).result
    -
    -    def reload(self):
    -        """Reload actors
    -        """
    -        self._load_actors(force_reload=True)
    -
    -    def execute(self, actor_name: str, actor_method: str, *args, die: bool = False, **kwargs) -> ActorResult:
    -        """Execute actor's method
    -
    -        Arguments:
    -            actor_name {str} -- actor name
    -            actor_method {str} -- actor method
    -
    -        Keyword Arguments:
    -            die {bool} --  flag to raise an error when request fails (default: {False})
    -
    -        Raises:
    -            RemoteException: Raises if the request failed and raise_on_error flag is set
    -
    -        Returns:
    -            ActorResult -- request result
    -        """
    -        payload = json.dumps((args, kwargs), default=serialize)
    -        response = self.redis_client.execute_command(actor_name, actor_method, payload)
    -
    -        deserializer = deserialize if not self.disable_deserialization else None
    -        response = json.loads(response, object_hook=deserializer)
    -
    -        if not response["success"]:
    -            if die or self.raise_on_error:
    -                raise RemoteException(response["error"])
    -
    -            response["error_type"] = GedisErrorTypes(response["error_type"])
    -
    -        return ActorResult(**response)
    -
    -
    -class RemoteException(Exception):
    -    pass
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class ActorProxy -(actor_name, actor_info, client) -
    -
    -

    ActorProxy to remote actor on the server side

    -

    Arguments

    -

    actor_name {str} – [description] -actor_info {dict} – actor information dict e.g { method_name: { args: [], 'doc':…} } -gedis_client {GedisClient} – gedis client reference

    -
    -Source code -
    class ActorProxy:
    -    def __init__(self, actor_name, actor_info, client):
    -        """ActorProxy to remote actor on the server side
    -
    -        Arguments:
    -            actor_name {str} -- [description]
    -            actor_info {dict} -- actor information dict e.g { method_name: { args: [], 'doc':...} }
    -            gedis_client {GedisClient} -- gedis client reference
    -        """
    -        self.actor_name = actor_name
    -        self.actor_info = actor_info
    -        self.client = client
    -
    -    def __dir__(self):
    -        """Delegate the available functions on the ActorProxy to `actor_info` keys
    -
    -        Returns:
    -            list -- methods available on the ActorProxy
    -        """
    -        return list(self.actor_info["methods"].keys())
    -
    -    def __getattr__(self, method):
    -        """Return a function representing the remote function on the actual actor
    -
    -        Arguments:
    -            attr {str} -- method name
    -
    -        Returns:
    -            function -- function waiting on the arguments
    -        """
    -
    -        def function(*args, **kwargs):
    -            return self.client.execute(self.actor_name, method, *args, **kwargs)
    -
    -        func = partial(function)
    -        func.__doc__ = self.actor_info["methods"][method]["doc"]
    -        return func
    -
    -
    -
    -class ActorResult -(**kwargs) -
    -
    -
    -
    -Source code -
    class ActorResult:
    -    def __init__(self, **kwargs):
    -        self.success = kwargs.get("success", True)
    -        self.result = kwargs.get("result", None)
    -        self.error = kwargs.get("error", None)
    -        self.error_type = kwargs.get("error_type", None)
    -        self.is_async = kwargs.get("is_async", False)
    -        self.task_id = kwargs.get("task_id", None)
    -
    -    def __dir__(self):
    -        return list(self.__dict__.keys())
    -
    -    def __repr__(self):
    -        return str(self.__dict__)
    -
    -
    -
    -class ActorsCollection -(actors) -
    -
    -
    -
    -Source code -
    class ActorsCollection:
    -    def __init__(self, actors):
    -        self._actors = actors
    -
    -    def __dir__(self):
    -        return list(self._actors.keys())
    -
    -    def __getattr__(self, actor_name):
    -        if actor_name in self._actors:
    -            return self._actors[actor_name]
    -
    -
    -
    -class GedisClient -(*args, **kwargs) -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class GedisClient(Client):
    -    name = fields.String(default="local")
    -    hostname = fields.String(default="localhost")
    -    port = fields.Integer(default=16000)
    -    raise_on_error = fields.Boolean(default=False)
    -    disable_deserialization = fields.Boolean(default=False)
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -        self._redisclient = None
    -        self._loaded_actors = {}
    -        self._loaded_modules = []
    -        self.actors = None
    -        self._load_actors()
    -
    -    @property
    -    def redis_client(self):
    -        if self._redisclient is None:
    -            self._redisclient = j.clients.redis.get(name=f"gedis_{self.name}", hostname=self.hostname, port=self.port)
    -        return self._redisclient
    -
    -    def _load_module(self, path, force_reload=False):
    -        load_python_module(path, force_reload=force_reload)
    -        if path not in self._loaded_modules:
    -            self._loaded_modules.append(path)
    -
    -    def _load_actors(self, force_reload=False):
    -        self._loaded_actors = {}
    -        for actor_name in self.list_actors():
    -            actor_info = self._get_actor_info(actor_name)
    -            self._load_module(actor_info["path"], force_reload=force_reload)
    -            self._loaded_actors[actor_name] = ActorProxy(actor_name, actor_info, self)
    -
    -        self.actors = ActorsCollection(self._loaded_actors)
    -
    -    def _get_actor_info(self, actor_name):
    -        return self.execute(actor_name, "info", die=True).result
    -
    -    def list_actors(self) -> list:
    -        """List actors
    -
    -        Returns:
    -            list -- List of loaded actors
    -        """
    -        return self.execute("core", "list_actors", die=True).result
    -
    -    def reload(self):
    -        """Reload actors
    -        """
    -        self._load_actors(force_reload=True)
    -
    -    def execute(self, actor_name: str, actor_method: str, *args, die: bool = False, **kwargs) -> ActorResult:
    -        """Execute actor's method
    -
    -        Arguments:
    -            actor_name {str} -- actor name
    -            actor_method {str} -- actor method
    -
    -        Keyword Arguments:
    -            die {bool} --  flag to raise an error when request fails (default: {False})
    -
    -        Raises:
    -            RemoteException: Raises if the request failed and raise_on_error flag is set
    -
    -        Returns:
    -            ActorResult -- request result
    -        """
    -        payload = json.dumps((args, kwargs), default=serialize)
    -        response = self.redis_client.execute_command(actor_name, actor_method, payload)
    -
    -        deserializer = deserialize if not self.disable_deserialization else None
    -        response = json.loads(response, object_hook=deserializer)
    -
    -        if not response["success"]:
    -            if die or self.raise_on_error:
    -                raise RemoteException(response["error"])
    -
    -            response["error_type"] = GedisErrorTypes(response["error_type"])
    -
    -        return ActorResult(**response)
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var disable_deserialization
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var hostname
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var name
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var port
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var raise_on_error
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var redis_client
    -
    -
    -
    -Source code -
    @property
    -def redis_client(self):
    -    if self._redisclient is None:
    -        self._redisclient = j.clients.redis.get(name=f"gedis_{self.name}", hostname=self.hostname, port=self.port)
    -    return self._redisclient
    -
    -
    -
    -

    Methods

    -
    -
    -def execute(self, actor_name, actor_method, *args, die=False, **kwargs) -
    -
    -

    Execute actor's method

    -

    Arguments

    -

    actor_name {str} – actor name -actor_method {str} – actor method -Keyword Arguments: -die {bool} – -flag to raise an error when request fails (default: {False})

    -

    Raises

    -
    -
    RemoteException
    -
    Raises if the request failed and raise_on_error flag is set
    -
    -

    Returns

    -
    -
    ActorResultrequest result
    -
     
    -
    -
    -Source code -
    def execute(self, actor_name: str, actor_method: str, *args, die: bool = False, **kwargs) -> ActorResult:
    -    """Execute actor's method
    -
    -    Arguments:
    -        actor_name {str} -- actor name
    -        actor_method {str} -- actor method
    -
    -    Keyword Arguments:
    -        die {bool} --  flag to raise an error when request fails (default: {False})
    -
    -    Raises:
    -        RemoteException: Raises if the request failed and raise_on_error flag is set
    -
    -    Returns:
    -        ActorResult -- request result
    -    """
    -    payload = json.dumps((args, kwargs), default=serialize)
    -    response = self.redis_client.execute_command(actor_name, actor_method, payload)
    -
    -    deserializer = deserialize if not self.disable_deserialization else None
    -    response = json.loads(response, object_hook=deserializer)
    -
    -    if not response["success"]:
    -        if die or self.raise_on_error:
    -            raise RemoteException(response["error"])
    -
    -        response["error_type"] = GedisErrorTypes(response["error_type"])
    -
    -    return ActorResult(**response)
    -
    -
    -
    -def list_actors(self) -
    -
    -

    List actors

    -

    Returns

    -
    -
    listList of loaded actors
    -
     
    -
    -
    -Source code -
    def list_actors(self) -> list:
    -    """List actors
    -
    -    Returns:
    -        list -- List of loaded actors
    -    """
    -    return self.execute("core", "list_actors", die=True).result
    -
    -
    -
    -def reload(self) -
    -
    -

    Reload actors

    -
    -Source code -
    def reload(self):
    -    """Reload actors
    -    """
    -    self._load_actors(force_reload=True)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class RemoteException -(*args, **kwargs) -
    -
    -

    Common base class for all non-exit exceptions.

    -
    -Source code -
    class RemoteException(Exception):
    -    pass
    -
    -

    Ancestors

    -
      -
    • builtins.Exception
    • -
    • builtins.BaseException
    • -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/clients/gedis/index.html b/docs/api/jumpscale/clients/gedis/index.html deleted file mode 100644 index 4dc00a8e5..000000000 --- a/docs/api/jumpscale/clients/gedis/index.html +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - -jumpscale.clients.gedis API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.gedis

    -
    -
    -

    This module gives you all the facilities to communicate with gedis server

    -

    Connecting to a gedis server

    -
    JS-NG> gedis = j.clients.gedis.get("local")
    -JS-NG> gedis.list_actors()
    -['system']
    -
    -

    Registering actor

    -
    JS-NG> gedis.actors.system.register_actor("greeter", "/home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py")
    -1
    -
    -

    Listing actors

    -
    JS-NG> gedis.list_actors()
    -['system', 'greeter']
    -
    -

    Documentation of an actor

    -
    JS-NG> gedis.ppdoc("greeter")
    -{
    -  "add2": {
    -    "args": [
    -      "a",
    -      "b"
    -    ],
    -    "doc": "Add two args
    -
    -        "
    -  },
    -  "hi": {
    -    "args": [],
    -    "doc": "returns hello world
    -        "
    -  },
    -  "info": {
    -    "args": [
    -      "result",
    -      "members",
    -      "name",
    -      "attr"
    -    ],
    -    "doc": ""
    -  },
    -  "ping": {
    -    "args": [],
    -    "doc": "
    -
    -        "
    -  }
    -}
    -
    -

    Invoking an actor method

    -
    JS-NG> gedis.execute("greeter", "hi")
    -b'hello world'
    -
    -JS-NG> gedis.execute("greeter", "ping")
    -b'pong no?'
    -
    -JS-NG> gedis.execute("greeter", "add2", "first", "second")
    -b'firstsecond'
    -
    -

    Invoking actor method with attribute access

    -
    JS-NG> gedis.actors.greeter.hi()
    -b'hello world'
    -
    -JS-NG> gedis.actors.greeter.add2("a", "b")
    -b'ab'
    -
    -
    -Source code -
    """This module gives you all the facilities to communicate with gedis server
    -
    -Connecting to a gedis server
    -```
    -JS-NG> gedis = j.clients.gedis.get("local")
    -JS-NG> gedis.list_actors()
    -['system']
    -```
    -
    -Registering actor
    -```
    -JS-NG> gedis.actors.system.register_actor("greeter", "/home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py")
    -1
    -```
    -
    -Listing actors
    -```
    -JS-NG> gedis.list_actors()
    -['system', 'greeter']
    -```
    -
    -Documentation of an actor
    -
    -```
    -JS-NG> gedis.ppdoc("greeter")
    -{
    -  "add2": {
    -    "args": [
    -      "a",
    -      "b"
    -    ],
    -    "doc": "Add two args\n        \n        "
    -  },
    -  "hi": {
    -    "args": [],
    -    "doc": "returns hello world\n        "
    -  },
    -  "info": {
    -    "args": [
    -      "result",
    -      "members",
    -      "name",
    -      "attr"
    -    ],
    -    "doc": ""
    -  },
    -  "ping": {
    -    "args": [],
    -    "doc": "\n        \n        "
    -  }
    -}
    -```
    -Invoking an actor method
    -```
    -JS-NG> gedis.execute("greeter", "hi")
    -b'hello world'
    -
    -JS-NG> gedis.execute("greeter", "ping")
    -b'pong no?'
    -
    -JS-NG> gedis.execute("greeter", "add2", "first", "second")
    -b'firstsecond'
    -```
    -Invoking actor method with attribute access
    -```
    -JS-NG> gedis.actors.greeter.hi()
    -b'hello world'
    -
    -JS-NG> gedis.actors.greeter.add2("a", "b")
    -b'ab'
    -```
    -"""
    -
    -
    -def export_module_as():
    -
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .gedis import GedisClient
    -
    -    return StoredFactory(GedisClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.gedis.gedis
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .gedis import GedisClient
    -
    -    return StoredFactory(GedisClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/clients/github/base.html b/docs/api/jumpscale/clients/github/base.html deleted file mode 100644 index a26cafd8e..000000000 --- a/docs/api/jumpscale/clients/github/base.html +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - -jumpscale.clients.github.base API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.github.base

    -
    -
    -
    -Source code -
    from jumpscale.clients.base import Client
    -from jumpscale.core.base import Base, fields
    -from jumpscale.loader import j
    -
    -replacelabels = {
    -    "bug": "type_bug",
    -    "duplicate": "process_duplicate",
    -    "enhancement": "type_feature",
    -    "help wanted": "state_question",
    -    "invalid": "state_question",
    -    "question": "state_question",
    -    "wontfix": "process_wontfix",
    -    "completed": "state_verification",
    -    "in progress": "state_inprogress",
    -    "ready": "state_verification",
    -    "story": "type_story",
    -    "urgent": "priority_urgent",
    -    "type_bug": "type_unknown",
    -    "type_story": "type_unknown",
    -}
    -
    -
    -class base(Base):
    -    def __init__(self):
    -        super().__init__()
    -
    -    @property
    -    def body_without_tags(self):
    -        # remove the tag lines from the body
    -        out = ""
    -        if self.body is None:
    -            return ""
    -        for line in self.body.split("\n"):
    -            if line.startswith("##") and not line.startswith("###"):
    -                continue
    -            out += "%s\n" % line
    -
    -        out = out.rstrip() + "\n"
    -        return out
    -
    -    # @tags.setter
    -    # def tags(self, ddict):
    -    #     if isinstance(ddict,dict) is False:
    -    #         raise Exception("Tags need to be dict as input for setter, now:%s" % ddict)
    -
    -    #     keys = sorted(ddict.keys())
    -
    -    #     out = self.body_without_tags + "\n"
    -    #     for key, val in ddict.items():
    -    #         out += ".. %s:%s\n" % (key, val)
    -
    -    #     self.body = out
    -    #     return self.tags
    -
    -    def __str__(self):
    -        return str(self._ddict)
    -
    -    __repr__ = __str__
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class base -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class base(Base):
    -    def __init__(self):
    -        super().__init__()
    -
    -    @property
    -    def body_without_tags(self):
    -        # remove the tag lines from the body
    -        out = ""
    -        if self.body is None:
    -            return ""
    -        for line in self.body.split("\n"):
    -            if line.startswith("##") and not line.startswith("###"):
    -                continue
    -            out += "%s\n" % line
    -
    -        out = out.rstrip() + "\n"
    -        return out
    -
    -    # @tags.setter
    -    # def tags(self, ddict):
    -    #     if isinstance(ddict,dict) is False:
    -    #         raise Exception("Tags need to be dict as input for setter, now:%s" % ddict)
    -
    -    #     keys = sorted(ddict.keys())
    -
    -    #     out = self.body_without_tags + "\n"
    -    #     for key, val in ddict.items():
    -    #         out += ".. %s:%s\n" % (key, val)
    -
    -    #     self.body = out
    -    #     return self.tags
    -
    -    def __str__(self):
    -        return str(self._ddict)
    -
    -    __repr__ = __str__
    -
    -

    Ancestors

    -
      -
    • Base
    • -
    • types.SimpleNamespace
    • -
    -

    Subclasses

    - -

    Instance variables

    -
    -
    var body_without_tags
    -
    -
    -
    -Source code -
    @property
    -def body_without_tags(self):
    -    # remove the tag lines from the body
    -    out = ""
    -    if self.body is None:
    -        return ""
    -    for line in self.body.split("\n"):
    -        if line.startswith("##") and not line.startswith("###"):
    -            continue
    -        out += "%s\n" % line
    -
    -    out = out.rstrip() + "\n"
    -    return out
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/github/github.html b/docs/api/jumpscale/clients/github/github.html deleted file mode 100644 index e66e0fff9..000000000 --- a/docs/api/jumpscale/clients/github/github.html +++ /dev/null @@ -1,462 +0,0 @@ - - - - - - -jumpscale.clients.github.github API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.github.github

    -
    -
    -
    -Source code -
    from jumpscale.clients.base import Client
    -from jumpscale.core.base import Base, fields
    -from jumpscale.loader import j
    -from .repo import GithubRepo
    -from github import Github, GithubObject
    -
    -NotSet = GithubObject.NotSet
    -
    -
    -class GithubClient(Client):
    -    username = fields.String()
    -    password = fields.String()
    -    accesstoken = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__client = None
    -
    -    @property
    -    def github_client(self):
    -        if not self.__client:
    -            if self.accesstoken:
    -                self.__client = Github(self.accesstoken)
    -            else:
    -                self.__client = Github(login_or_token=self.username, password=self.password)
    -        return self.__client
    -
    -    def get_repo(self, repo_full_name):
    -        return GithubRepo(self.__client,repo_full_name)
    -
    -    def get_repos(self):
    -        l = []
    -        for r in self.github_client.get_user().get_repos():
    -            l.append(GithubRepo(self.__client,r.full_name))
    -        return l
    -
    -    def get_orgs(self):
    -        l = []
    -        for o in self.github_client.get_user().get_orgs():
    -            l.append(o.login)
    -        return l
    -
    -    def get_userdata(self):
    -        u = self.github_client.get_user()
    -        el = []
    -        for e in u.get_emails():
    -            el.append(e)
    -        return {"name": u.name, "emails": el, "id": u.id, "avatar_url": u.avatar_url}
    -
    -    def create_repo(
    -        self,
    -        name,
    -        description=NotSet,
    -        homepage=NotSet,
    -        private=NotSet,
    -        has_issues=NotSet,
    -        has_wiki=NotSet,
    -        has_downloads=NotSet,
    -        auto_init=NotSet,
    -        gitignore_template=NotSet,
    -    ):
    -
    -        return self.github_client.get_user().create_repo(
    -            name,
    -            description=description,
    -            homepage=homepage,
    -            private=private,
    -            has_issues=has_issues,
    -            has_wiki=has_wiki,
    -            has_downloads=has_downloads,
    -            auto_init=auto_init,
    -            gitignore_template=gitignore_template,
    -        )
    -
    -    def delete_repo(self, repo_name):
    -        return self.__client.get_user().get_repo(repo_name).delete()
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class GithubClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class GithubClient(Client):
    -    username = fields.String()
    -    password = fields.String()
    -    accesstoken = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__client = None
    -
    -    @property
    -    def github_client(self):
    -        if not self.__client:
    -            if self.accesstoken:
    -                self.__client = Github(self.accesstoken)
    -            else:
    -                self.__client = Github(login_or_token=self.username, password=self.password)
    -        return self.__client
    -
    -    def get_repo(self, repo_full_name):
    -        return GithubRepo(self.__client,repo_full_name)
    -
    -    def get_repos(self):
    -        l = []
    -        for r in self.github_client.get_user().get_repos():
    -            l.append(GithubRepo(self.__client,r.full_name))
    -        return l
    -
    -    def get_orgs(self):
    -        l = []
    -        for o in self.github_client.get_user().get_orgs():
    -            l.append(o.login)
    -        return l
    -
    -    def get_userdata(self):
    -        u = self.github_client.get_user()
    -        el = []
    -        for e in u.get_emails():
    -            el.append(e)
    -        return {"name": u.name, "emails": el, "id": u.id, "avatar_url": u.avatar_url}
    -
    -    def create_repo(
    -        self,
    -        name,
    -        description=NotSet,
    -        homepage=NotSet,
    -        private=NotSet,
    -        has_issues=NotSet,
    -        has_wiki=NotSet,
    -        has_downloads=NotSet,
    -        auto_init=NotSet,
    -        gitignore_template=NotSet,
    -    ):
    -
    -        return self.github_client.get_user().create_repo(
    -            name,
    -            description=description,
    -            homepage=homepage,
    -            private=private,
    -            has_issues=has_issues,
    -            has_wiki=has_wiki,
    -            has_downloads=has_downloads,
    -            auto_init=auto_init,
    -            gitignore_template=gitignore_template,
    -        )
    -
    -    def delete_repo(self, repo_name):
    -        return self.__client.get_user().get_repo(repo_name).delete()
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var accesstoken
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var github_client
    -
    -
    -
    -Source code -
    @property
    -def github_client(self):
    -    if not self.__client:
    -        if self.accesstoken:
    -            self.__client = Github(self.accesstoken)
    -        else:
    -            self.__client = Github(login_or_token=self.username, password=self.password)
    -    return self.__client
    -
    -
    -
    var password
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var username
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def create_repo(self, name, description=NotSet, homepage=NotSet, private=NotSet, has_issues=NotSet, has_wiki=NotSet, has_downloads=NotSet, auto_init=NotSet, gitignore_template=NotSet) -
    -
    -
    -
    -Source code -
    def create_repo(
    -    self,
    -    name,
    -    description=NotSet,
    -    homepage=NotSet,
    -    private=NotSet,
    -    has_issues=NotSet,
    -    has_wiki=NotSet,
    -    has_downloads=NotSet,
    -    auto_init=NotSet,
    -    gitignore_template=NotSet,
    -):
    -
    -    return self.github_client.get_user().create_repo(
    -        name,
    -        description=description,
    -        homepage=homepage,
    -        private=private,
    -        has_issues=has_issues,
    -        has_wiki=has_wiki,
    -        has_downloads=has_downloads,
    -        auto_init=auto_init,
    -        gitignore_template=gitignore_template,
    -    )
    -
    -
    -
    -def delete_repo(self, repo_name) -
    -
    -
    -
    -Source code -
    def delete_repo(self, repo_name):
    -    return self.__client.get_user().get_repo(repo_name).delete()
    -
    -
    -
    -def get_orgs(self) -
    -
    -
    -
    -Source code -
    def get_orgs(self):
    -    l = []
    -    for o in self.github_client.get_user().get_orgs():
    -        l.append(o.login)
    -    return l
    -
    -
    -
    -def get_repo(self, repo_full_name) -
    -
    -
    -
    -Source code -
    def get_repo(self, repo_full_name):
    -    return GithubRepo(self.__client,repo_full_name)
    -
    -
    -
    -def get_repos(self) -
    -
    -
    -
    -Source code -
    def get_repos(self):
    -    l = []
    -    for r in self.github_client.get_user().get_repos():
    -        l.append(GithubRepo(self.__client,r.full_name))
    -    return l
    -
    -
    -
    -def get_userdata(self) -
    -
    -
    -
    -Source code -
    def get_userdata(self):
    -    u = self.github_client.get_user()
    -    el = []
    -    for e in u.get_emails():
    -        el.append(e)
    -    return {"name": u.name, "emails": el, "id": u.id, "avatar_url": u.avatar_url}
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/github/index.html b/docs/api/jumpscale/clients/github/index.html deleted file mode 100644 index df2c483d0..000000000 --- a/docs/api/jumpscale/clients/github/index.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - -jumpscale.clients.github API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.github

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .github import GithubClient
    -
    -    return StoredFactory(GithubClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.github.base
    -
    -
    -
    -
    jumpscale.clients.github.github
    -
    -
    -
    -
    jumpscale.clients.github.issue
    -
    -
    -
    -
    jumpscale.clients.github.milestone
    -
    -
    -
    -
    jumpscale.clients.github.repo
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .github import GithubClient
    -
    -    return StoredFactory(GithubClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/github/issue.html b/docs/api/jumpscale/clients/github/issue.html deleted file mode 100644 index 67e1d1277..000000000 --- a/docs/api/jumpscale/clients/github/issue.html +++ /dev/null @@ -1,1090 +0,0 @@ - - - - - - -jumpscale.clients.github.issue API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.github.issue

    -
    -
    -
    -Source code -
    from jumpscale.loader import j
    -from .base import base
    -from .base import replacelabels
    -from .milestone import RepoMilestone
    -
    -
    -class Issue(base):
    -    def __init__(self, repo, ddict={}, githubObj=None):
    -        base.__init__(self)
    -        self.repo = repo
    -        self._ddict = ddict
    -        self._githubObj = githubObj
    -        self._comments = ddict.get("comments", None)
    -        if githubObj is not None:
    -            self.load()
    -
    -        self._lock = threading.RLock()
    -        # self.todo
    -
    -    @property
    -    def api(self):
    -        if self._githubObj is None:
    -            self._githubObj = self.repo.api.get_issue(self.number)
    -        return self._githubObj
    -
    -    @property
    -    def ddict(self):
    -        if self._ddict == {}:
    -            # no dict yet, fetch from github
    -            self.load()
    -        # we lazy load the comments. so it's only loaded when accesses
    -        self._ddict["comments"] = self.comments
    -        return self._ddict
    -
    -    @property
    -    def comments(self):
    -        if self._comments is not None:
    -            return self._comments
    -
    -        with self._lock:
    -            if self._comments is None:
    -                self._log_debug("Loading comments for issue: %s" % self.number)
    -                self._comments = []
    -                for comment in self.api.get_comments():
    -                    obj = {}
    -                    user = self.repo.client.getUserLogin(githubObj=comment.user)
    -                    obj["user"] = user
    -                    obj["url"] = comment.url
    -                    obj["id"] = comment.id
    -                    obj["body"] = comment.body
    -                    obj["user_id"] = comment.user.id
    -                    # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
    -                    self._comments.append(obj)
    -        return self._comments
    -
    -    def reload_comments(self):
    -        with self._lock:
    -            self._comments = []
    -            for comment in self.api.get_comments():
    -                obj = {}
    -                user = self.repo.client.getUserLogin(githubObj=comment.user)
    -                obj["user"] = user
    -                obj["url"] = comment.url
    -                obj["id"] = comment.id
    -                obj["body"] = comment.body
    -                # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
    -                self._comments.append(obj)
    -        return self._comments
    -
    -    @property
    -    def guid(self):
    -        return self.repo.fullname + "_" + str(self._ddict["number"])
    -
    -    @property
    -    def number(self):
    -        return int(self._ddict["number"])
    -
    -    @property
    -    def title(self):
    -        return self._ddict["title"]
    -
    -    @property
    -    def body(self):
    -        return self._ddict["body"]
    -
    -    @body.setter
    -    def body(self, val):
    -        self._ddict["body"] = val
    -        try:
    -            self.api.edit(body=self._ddict["body"])
    -        except Exception as e:
    -            self._log_error("Failed to update the issue body: %s" % e)
    -
    -    @property
    -    def time(self):
    -        return self._ddict["time"]
    -
    -    @property
    -    def url(self):
    -        return self._ddict["url"]
    -
    -    @property
    -    def assignee(self):
    -        return self._ddict["assignee"]
    -
    -    @property
    -    def labels(self):
    -        # we return a copy so changing the list doesn't actually change the
    -        # ddict value
    -        return self._ddict["labels"][:]
    -
    -    @property
    -    def id(self):
    -        return self._ddict["id"]
    -
    -    @labels.setter
    -    def labels(self, val):
    -        # check if all are already in labels, if yes nothing to do
    -        if len(val) == len(self._ddict["labels"]):
    -            self._ddict["labels"].sort()
    -            val.sort()
    -            if val == self._ddict["labels"]:
    -                return
    -        self._ddict["labels"] = val
    -        toset = [self.repo.getLabel(item) for item in self._ddict["labels"]]
    -        self.api.set_labels(*toset)
    -
    -    @property
    -    def milestone(self):
    -        return self._ddict["milestone"]
    -
    -    @property
    -    def state(self):
    -        states = []
    -        if not self.is_open:
    -            return "closed"
    -
    -        for label in self.labels:
    -            if label.startswith("state"):
    -                states.append(label)
    -        if len(states) == 1:
    -            return states[0][len("state") :].strip("_")
    -        elif len(states) > 1:
    -            self.state = "question"
    -        else:
    -            return ""
    -
    -    @state.setter
    -    def state(self, val):
    -        return self._setLabels(val, "state")
    -
    -    @property
    -    def is_open(self):
    -        return self._ddict["open"]
    -
    -    @property
    -    def type(self):
    -        items = []
    -        for label in self.labels:
    -            if label.startswith("type"):
    -                items.append(label)
    -        if len(items) == 1:
    -            return items[0].partition("_")[-1]
    -
    -        return ""
    -
    -    @type.setter
    -    def type(self, val):
    -        return self._setLabels(val, "type")
    -
    -    @property
    -    def priority(self):
    -        items = []
    -        for label in self.labels:
    -            if label.startswith("priority"):
    -                items.append(label)
    -        if len(items) == 1:
    -            return items[0].partition("_")[-1]
    -        else:
    -            self.priority = "normal"
    -            return self.priority
    -
    -    @priority.setter
    -    def priority(self, val):
    -        return self._setLabels(val, "priority")
    -
    -    @property
    -    def process(self):
    -        items = []
    -        for label in self.labels:
    -            if label.startswith("process"):
    -                items.append(label)
    -        if len(items) == 1:
    -            return items[0][len("process") :].strip("_")
    -        else:
    -            return ""
    -
    -    @process.setter
    -    def process(self, val):
    -        return self._setLabels(val, "process")
    -
    -    def _setLabels(self, val, category):
    -        if val is None or val == "":
    -            return
    -
    -        if val.startswith(category):
    -            _, _, val = val.partition("_")
    -
    -        val = val.strip("_")
    -        val = val.lower()
    -
    -        val = "%s_%s" % (category, val)
    -
    -        if val not in self.repo.labelnames:
    -            self.repo.labelnames.sort()
    -            llist = ",".join(self.repo.labelnames)
    -            raise Exception(
    -                "Label needs to be in list:%s (is understood labels in this repo on github), now is: '%s'"
    -                % (llist, val)
    -            )
    -
    -        # make sure there is only 1
    -        labels2set = self.labels
    -        items = []
    -        for label in self.labels:
    -            if label.startswith(category):
    -                items.append(label)
    -        if len(items) == 1 and val in items:
    -            return
    -        for item in items:
    -            labels2set.pop(labels2set.index(item))
    -        if val is not None or val != "":
    -            labels2set.append(val)
    -        self.labels = labels2set
    -
    -    def load(self):
    -
    -        self._ddict = {}
    -
    -        # check labels
    -        labels = [item.name for item in self.api.labels]  # are the names
    -        newlabels = []
    -        for label in labels:
    -            if label not in self.repo.labelnames:
    -                if label in replacelabels:
    -                    if replacelabels[label] not in newlabels:
    -                        newlabels.append(replacelabels[label])
    -            else:
    -                if label not in newlabels:
    -                    newlabels.append(label)
    -
    -        if labels != newlabels:
    -            self._log_info("change label:%s for %s" % (labels, self.api.title))
    -            labels2set = [self.repo.getLabel(item) for item in newlabels]
    -            self.api.set_labels(*labels2set)
    -            labels = newlabels
    -
    -        self._ddict["labels"] = labels
    -        self._ddict["id"] = self.api.id
    -        self._ddict["url"] = self.api.html_url
    -        self._ddict["number"] = self.api.number
    -        self._ddict["open"] = self.api.state == "open"
    -
    -        self._ddict["assignee"] = self.repo.client.getUserLogin(githubObj=self.api.assignee)
    -        self._ddict["state"] = self.api.state
    -        self._ddict["title"] = self.api.title
    -
    -        self._ddict["body"] = self.api.body
    -
    -        # self._ddict["time"] = j.data.time.any2HRDateTime([self.api.last_modified, self.api.created_at])
    -
    -        self._log_debug("LOAD:%s %s" % (self.repo.fullname, self._ddict["title"]))
    -
    -        if self.api.milestone is None:
    -            self._ddict["milestone"] = ""
    -        else:
    -            ms = RepoMilestone(repo=self.repo, githubObj=self.api.milestone)
    -            self._ddict["milestone"] = "%s:%s" % (ms.number, ms.title)
    -
    -    @property
    -    def todo(self):
    -        if "_todo" not in self.__dict__:
    -            todo = []
    -            if self.body is not None:
    -                for line in self.body.split("\n"):
    -                    if line.startswith("!! "):
    -                        todo.append(line.strip().strip("!! "))
    -            for comment in self.comments:
    -                for line in comment["body"].split("\n"):
    -                    if line.startswith("!! "):
    -                        todo.append(line.strip().strip("!! "))
    -            self._todo = todo
    -        return self._todo
    -
    -    @property
    -    def istask(self):
    -        if self.type == "task" or self.title.lower().endswith("task"):
    -            return True
    -        return False
    -
    -    def __str__(self):
    -        return "issue:%s" % self.title
    -
    -    __repr__ = __str__
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Issue -(repo, ddict={}, githubObj=None) -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class Issue(base):
    -    def __init__(self, repo, ddict={}, githubObj=None):
    -        base.__init__(self)
    -        self.repo = repo
    -        self._ddict = ddict
    -        self._githubObj = githubObj
    -        self._comments = ddict.get("comments", None)
    -        if githubObj is not None:
    -            self.load()
    -
    -        self._lock = threading.RLock()
    -        # self.todo
    -
    -    @property
    -    def api(self):
    -        if self._githubObj is None:
    -            self._githubObj = self.repo.api.get_issue(self.number)
    -        return self._githubObj
    -
    -    @property
    -    def ddict(self):
    -        if self._ddict == {}:
    -            # no dict yet, fetch from github
    -            self.load()
    -        # we lazy load the comments. so it's only loaded when accesses
    -        self._ddict["comments"] = self.comments
    -        return self._ddict
    -
    -    @property
    -    def comments(self):
    -        if self._comments is not None:
    -            return self._comments
    -
    -        with self._lock:
    -            if self._comments is None:
    -                self._log_debug("Loading comments for issue: %s" % self.number)
    -                self._comments = []
    -                for comment in self.api.get_comments():
    -                    obj = {}
    -                    user = self.repo.client.getUserLogin(githubObj=comment.user)
    -                    obj["user"] = user
    -                    obj["url"] = comment.url
    -                    obj["id"] = comment.id
    -                    obj["body"] = comment.body
    -                    obj["user_id"] = comment.user.id
    -                    # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
    -                    self._comments.append(obj)
    -        return self._comments
    -
    -    def reload_comments(self):
    -        with self._lock:
    -            self._comments = []
    -            for comment in self.api.get_comments():
    -                obj = {}
    -                user = self.repo.client.getUserLogin(githubObj=comment.user)
    -                obj["user"] = user
    -                obj["url"] = comment.url
    -                obj["id"] = comment.id
    -                obj["body"] = comment.body
    -                # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
    -                self._comments.append(obj)
    -        return self._comments
    -
    -    @property
    -    def guid(self):
    -        return self.repo.fullname + "_" + str(self._ddict["number"])
    -
    -    @property
    -    def number(self):
    -        return int(self._ddict["number"])
    -
    -    @property
    -    def title(self):
    -        return self._ddict["title"]
    -
    -    @property
    -    def body(self):
    -        return self._ddict["body"]
    -
    -    @body.setter
    -    def body(self, val):
    -        self._ddict["body"] = val
    -        try:
    -            self.api.edit(body=self._ddict["body"])
    -        except Exception as e:
    -            self._log_error("Failed to update the issue body: %s" % e)
    -
    -    @property
    -    def time(self):
    -        return self._ddict["time"]
    -
    -    @property
    -    def url(self):
    -        return self._ddict["url"]
    -
    -    @property
    -    def assignee(self):
    -        return self._ddict["assignee"]
    -
    -    @property
    -    def labels(self):
    -        # we return a copy so changing the list doesn't actually change the
    -        # ddict value
    -        return self._ddict["labels"][:]
    -
    -    @property
    -    def id(self):
    -        return self._ddict["id"]
    -
    -    @labels.setter
    -    def labels(self, val):
    -        # check if all are already in labels, if yes nothing to do
    -        if len(val) == len(self._ddict["labels"]):
    -            self._ddict["labels"].sort()
    -            val.sort()
    -            if val == self._ddict["labels"]:
    -                return
    -        self._ddict["labels"] = val
    -        toset = [self.repo.getLabel(item) for item in self._ddict["labels"]]
    -        self.api.set_labels(*toset)
    -
    -    @property
    -    def milestone(self):
    -        return self._ddict["milestone"]
    -
    -    @property
    -    def state(self):
    -        states = []
    -        if not self.is_open:
    -            return "closed"
    -
    -        for label in self.labels:
    -            if label.startswith("state"):
    -                states.append(label)
    -        if len(states) == 1:
    -            return states[0][len("state") :].strip("_")
    -        elif len(states) > 1:
    -            self.state = "question"
    -        else:
    -            return ""
    -
    -    @state.setter
    -    def state(self, val):
    -        return self._setLabels(val, "state")
    -
    -    @property
    -    def is_open(self):
    -        return self._ddict["open"]
    -
    -    @property
    -    def type(self):
    -        items = []
    -        for label in self.labels:
    -            if label.startswith("type"):
    -                items.append(label)
    -        if len(items) == 1:
    -            return items[0].partition("_")[-1]
    -
    -        return ""
    -
    -    @type.setter
    -    def type(self, val):
    -        return self._setLabels(val, "type")
    -
    -    @property
    -    def priority(self):
    -        items = []
    -        for label in self.labels:
    -            if label.startswith("priority"):
    -                items.append(label)
    -        if len(items) == 1:
    -            return items[0].partition("_")[-1]
    -        else:
    -            self.priority = "normal"
    -            return self.priority
    -
    -    @priority.setter
    -    def priority(self, val):
    -        return self._setLabels(val, "priority")
    -
    -    @property
    -    def process(self):
    -        items = []
    -        for label in self.labels:
    -            if label.startswith("process"):
    -                items.append(label)
    -        if len(items) == 1:
    -            return items[0][len("process") :].strip("_")
    -        else:
    -            return ""
    -
    -    @process.setter
    -    def process(self, val):
    -        return self._setLabels(val, "process")
    -
    -    def _setLabels(self, val, category):
    -        if val is None or val == "":
    -            return
    -
    -        if val.startswith(category):
    -            _, _, val = val.partition("_")
    -
    -        val = val.strip("_")
    -        val = val.lower()
    -
    -        val = "%s_%s" % (category, val)
    -
    -        if val not in self.repo.labelnames:
    -            self.repo.labelnames.sort()
    -            llist = ",".join(self.repo.labelnames)
    -            raise Exception(
    -                "Label needs to be in list:%s (is understood labels in this repo on github), now is: '%s'"
    -                % (llist, val)
    -            )
    -
    -        # make sure there is only 1
    -        labels2set = self.labels
    -        items = []
    -        for label in self.labels:
    -            if label.startswith(category):
    -                items.append(label)
    -        if len(items) == 1 and val in items:
    -            return
    -        for item in items:
    -            labels2set.pop(labels2set.index(item))
    -        if val is not None or val != "":
    -            labels2set.append(val)
    -        self.labels = labels2set
    -
    -    def load(self):
    -
    -        self._ddict = {}
    -
    -        # check labels
    -        labels = [item.name for item in self.api.labels]  # are the names
    -        newlabels = []
    -        for label in labels:
    -            if label not in self.repo.labelnames:
    -                if label in replacelabels:
    -                    if replacelabels[label] not in newlabels:
    -                        newlabels.append(replacelabels[label])
    -            else:
    -                if label not in newlabels:
    -                    newlabels.append(label)
    -
    -        if labels != newlabels:
    -            self._log_info("change label:%s for %s" % (labels, self.api.title))
    -            labels2set = [self.repo.getLabel(item) for item in newlabels]
    -            self.api.set_labels(*labels2set)
    -            labels = newlabels
    -
    -        self._ddict["labels"] = labels
    -        self._ddict["id"] = self.api.id
    -        self._ddict["url"] = self.api.html_url
    -        self._ddict["number"] = self.api.number
    -        self._ddict["open"] = self.api.state == "open"
    -
    -        self._ddict["assignee"] = self.repo.client.getUserLogin(githubObj=self.api.assignee)
    -        self._ddict["state"] = self.api.state
    -        self._ddict["title"] = self.api.title
    -
    -        self._ddict["body"] = self.api.body
    -
    -        # self._ddict["time"] = j.data.time.any2HRDateTime([self.api.last_modified, self.api.created_at])
    -
    -        self._log_debug("LOAD:%s %s" % (self.repo.fullname, self._ddict["title"]))
    -
    -        if self.api.milestone is None:
    -            self._ddict["milestone"] = ""
    -        else:
    -            ms = RepoMilestone(repo=self.repo, githubObj=self.api.milestone)
    -            self._ddict["milestone"] = "%s:%s" % (ms.number, ms.title)
    -
    -    @property
    -    def todo(self):
    -        if "_todo" not in self.__dict__:
    -            todo = []
    -            if self.body is not None:
    -                for line in self.body.split("\n"):
    -                    if line.startswith("!! "):
    -                        todo.append(line.strip().strip("!! "))
    -            for comment in self.comments:
    -                for line in comment["body"].split("\n"):
    -                    if line.startswith("!! "):
    -                        todo.append(line.strip().strip("!! "))
    -            self._todo = todo
    -        return self._todo
    -
    -    @property
    -    def istask(self):
    -        if self.type == "task" or self.title.lower().endswith("task"):
    -            return True
    -        return False
    -
    -    def __str__(self):
    -        return "issue:%s" % self.title
    -
    -    __repr__ = __str__
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var api
    -
    -
    -
    -Source code -
    @property
    -def api(self):
    -    if self._githubObj is None:
    -        self._githubObj = self.repo.api.get_issue(self.number)
    -    return self._githubObj
    -
    -
    -
    var assignee
    -
    -
    -
    -Source code -
    @property
    -def assignee(self):
    -    return self._ddict["assignee"]
    -
    -
    -
    var body
    -
    -
    -
    -Source code -
    @property
    -def body(self):
    -    return self._ddict["body"]
    -
    -
    -
    var comments
    -
    -
    -
    -Source code -
    @property
    -def comments(self):
    -    if self._comments is not None:
    -        return self._comments
    -
    -    with self._lock:
    -        if self._comments is None:
    -            self._log_debug("Loading comments for issue: %s" % self.number)
    -            self._comments = []
    -            for comment in self.api.get_comments():
    -                obj = {}
    -                user = self.repo.client.getUserLogin(githubObj=comment.user)
    -                obj["user"] = user
    -                obj["url"] = comment.url
    -                obj["id"] = comment.id
    -                obj["body"] = comment.body
    -                obj["user_id"] = comment.user.id
    -                # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
    -                self._comments.append(obj)
    -    return self._comments
    -
    -
    -
    var ddict
    -
    -
    -
    -Source code -
    @property
    -def ddict(self):
    -    if self._ddict == {}:
    -        # no dict yet, fetch from github
    -        self.load()
    -    # we lazy load the comments. so it's only loaded when accesses
    -    self._ddict["comments"] = self.comments
    -    return self._ddict
    -
    -
    -
    var guid
    -
    -
    -
    -Source code -
    @property
    -def guid(self):
    -    return self.repo.fullname + "_" + str(self._ddict["number"])
    -
    -
    -
    var id
    -
    -
    -
    -Source code -
    @property
    -def id(self):
    -    return self._ddict["id"]
    -
    -
    -
    var is_open
    -
    -
    -
    -Source code -
    @property
    -def is_open(self):
    -    return self._ddict["open"]
    -
    -
    -
    var istask
    -
    -
    -
    -Source code -
    @property
    -def istask(self):
    -    if self.type == "task" or self.title.lower().endswith("task"):
    -        return True
    -    return False
    -
    -
    -
    var labels
    -
    -
    -
    -Source code -
    @property
    -def labels(self):
    -    # we return a copy so changing the list doesn't actually change the
    -    # ddict value
    -    return self._ddict["labels"][:]
    -
    -
    -
    var milestone
    -
    -
    -
    -Source code -
    @property
    -def milestone(self):
    -    return self._ddict["milestone"]
    -
    -
    -
    var number
    -
    -
    -
    -Source code -
    @property
    -def number(self):
    -    return int(self._ddict["number"])
    -
    -
    -
    var priority
    -
    -
    -
    -Source code -
    @property
    -def priority(self):
    -    items = []
    -    for label in self.labels:
    -        if label.startswith("priority"):
    -            items.append(label)
    -    if len(items) == 1:
    -        return items[0].partition("_")[-1]
    -    else:
    -        self.priority = "normal"
    -        return self.priority
    -
    -
    -
    var process
    -
    -
    -
    -Source code -
    @property
    -def process(self):
    -    items = []
    -    for label in self.labels:
    -        if label.startswith("process"):
    -            items.append(label)
    -    if len(items) == 1:
    -        return items[0][len("process") :].strip("_")
    -    else:
    -        return ""
    -
    -
    -
    var state
    -
    -
    -
    -Source code -
    @property
    -def state(self):
    -    states = []
    -    if not self.is_open:
    -        return "closed"
    -
    -    for label in self.labels:
    -        if label.startswith("state"):
    -            states.append(label)
    -    if len(states) == 1:
    -        return states[0][len("state") :].strip("_")
    -    elif len(states) > 1:
    -        self.state = "question"
    -    else:
    -        return ""
    -
    -
    -
    var time
    -
    -
    -
    -Source code -
    @property
    -def time(self):
    -    return self._ddict["time"]
    -
    -
    -
    var title
    -
    -
    -
    -Source code -
    @property
    -def title(self):
    -    return self._ddict["title"]
    -
    -
    -
    var todo
    -
    -
    -
    -Source code -
    @property
    -def todo(self):
    -    if "_todo" not in self.__dict__:
    -        todo = []
    -        if self.body is not None:
    -            for line in self.body.split("\n"):
    -                if line.startswith("!! "):
    -                    todo.append(line.strip().strip("!! "))
    -        for comment in self.comments:
    -            for line in comment["body"].split("\n"):
    -                if line.startswith("!! "):
    -                    todo.append(line.strip().strip("!! "))
    -        self._todo = todo
    -    return self._todo
    -
    -
    -
    var type
    -
    -
    -
    -Source code -
    @property
    -def type(self):
    -    items = []
    -    for label in self.labels:
    -        if label.startswith("type"):
    -            items.append(label)
    -    if len(items) == 1:
    -        return items[0].partition("_")[-1]
    -
    -    return ""
    -
    -
    -
    var url
    -
    -
    -
    -Source code -
    @property
    -def url(self):
    -    return self._ddict["url"]
    -
    -
    -
    -

    Methods

    -
    -
    -def load(self) -
    -
    -
    -
    -Source code -
    def load(self):
    -
    -    self._ddict = {}
    -
    -    # check labels
    -    labels = [item.name for item in self.api.labels]  # are the names
    -    newlabels = []
    -    for label in labels:
    -        if label not in self.repo.labelnames:
    -            if label in replacelabels:
    -                if replacelabels[label] not in newlabels:
    -                    newlabels.append(replacelabels[label])
    -        else:
    -            if label not in newlabels:
    -                newlabels.append(label)
    -
    -    if labels != newlabels:
    -        self._log_info("change label:%s for %s" % (labels, self.api.title))
    -        labels2set = [self.repo.getLabel(item) for item in newlabels]
    -        self.api.set_labels(*labels2set)
    -        labels = newlabels
    -
    -    self._ddict["labels"] = labels
    -    self._ddict["id"] = self.api.id
    -    self._ddict["url"] = self.api.html_url
    -    self._ddict["number"] = self.api.number
    -    self._ddict["open"] = self.api.state == "open"
    -
    -    self._ddict["assignee"] = self.repo.client.getUserLogin(githubObj=self.api.assignee)
    -    self._ddict["state"] = self.api.state
    -    self._ddict["title"] = self.api.title
    -
    -    self._ddict["body"] = self.api.body
    -
    -    # self._ddict["time"] = j.data.time.any2HRDateTime([self.api.last_modified, self.api.created_at])
    -
    -    self._log_debug("LOAD:%s %s" % (self.repo.fullname, self._ddict["title"]))
    -
    -    if self.api.milestone is None:
    -        self._ddict["milestone"] = ""
    -    else:
    -        ms = RepoMilestone(repo=self.repo, githubObj=self.api.milestone)
    -        self._ddict["milestone"] = "%s:%s" % (ms.number, ms.title)
    -
    -
    -
    -def reload_comments(self) -
    -
    -
    -
    -Source code -
    def reload_comments(self):
    -    with self._lock:
    -        self._comments = []
    -        for comment in self.api.get_comments():
    -            obj = {}
    -            user = self.repo.client.getUserLogin(githubObj=comment.user)
    -            obj["user"] = user
    -            obj["url"] = comment.url
    -            obj["id"] = comment.id
    -            obj["body"] = comment.body
    -            # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
    -            self._comments.append(obj)
    -    return self._comments
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/github/milestone.html b/docs/api/jumpscale/clients/github/milestone.html deleted file mode 100644 index fefdaf0a0..000000000 --- a/docs/api/jumpscale/clients/github/milestone.html +++ /dev/null @@ -1,368 +0,0 @@ - - - - - - -jumpscale.clients.github.milestone API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.github.milestone

    -
    -
    -
    -Source code -
    from jumpscale.loader import j
    -from .base import base
    -
    -
    -class RepoMilestone(base):
    -    """
    -    milestone as defined on 1 specific repo
    -    """
    -
    -    def __init__(self, repo, githubObj=None):
    -        base.__init__(self)
    -        self._ddict = {}
    -        self._githubObj = githubObj
    -        if githubObj is not None:
    -            self.load()
    -        self.repo = repo
    -
    -    # @property
    -    # def api(self):
    -    #     if self._githubObj is None:
    -    #         j.application.break_into_jshell("DEBUG NOW get api for milestone")
    -    #     return self._githubObj
    -
    -    def load(self):
    -        self._ddict = {}
    -        #self._ddict["deadline"] = j.data.time.any2HRDateTime(self.api.due_on)
    -        self._ddict["id"] = self.api.id
    -        self._ddict["url"] = self.api.url
    -        self._ddict["title"] = self.api.title
    -        self._ddict["body"] = self.api.description
    -        self._ddict["number"] = self.api.number
    -
    -    @property
    -    def title(self):
    -        return self._ddict["title"]
    -
    -    @title.setter
    -    def title(self, val):
    -        self._ddict["title"] = val
    -        self.api.edit(title=val)
    -
    -    @property
    -    def ddict(self):
    -        if not self._ddict:
    -            # no dict yet, fetch from github
    -            self.load()
    -        return self._ddict
    -
    -
    -
    -    # synonym to let the tags of super class work
    -    @property
    -    def body(self):
    -        return self._ddict["body"]
    -
    -    @body.setter
    -    def body(self, val):
    -        if self._ddict["body"] != val:
    -            self._ddict["body"] = val
    -            self.api.edit(self.title, description=val)
    -
    -    # @property
    -    # def deadline(self):
    -    #     return self._ddict["deadline"]
    -
    -    # @deadline.setter
    -    # def deadline(self, val):
    -    #     #due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(val)))
    -
    -    #     self._ddict["deadline"] = val
    -    #     self.api.edit(title=self.title)
    -
    -    @property
    -    def id(self):
    -        return self._ddict["id"]
    -
    -    @property
    -    def url(self):
    -        return self._ddict["url"]
    -
    -    @property
    -    def number(self):
    -        return self._ddict["number"]
    -        
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class RepoMilestone -(repo, githubObj=None) -
    -
    -

    milestone as defined on 1 specific repo

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class RepoMilestone(base):
    -    """
    -    milestone as defined on 1 specific repo
    -    """
    -
    -    def __init__(self, repo, githubObj=None):
    -        base.__init__(self)
    -        self._ddict = {}
    -        self._githubObj = githubObj
    -        if githubObj is not None:
    -            self.load()
    -        self.repo = repo
    -
    -    # @property
    -    # def api(self):
    -    #     if self._githubObj is None:
    -    #         j.application.break_into_jshell("DEBUG NOW get api for milestone")
    -    #     return self._githubObj
    -
    -    def load(self):
    -        self._ddict = {}
    -        #self._ddict["deadline"] = j.data.time.any2HRDateTime(self.api.due_on)
    -        self._ddict["id"] = self.api.id
    -        self._ddict["url"] = self.api.url
    -        self._ddict["title"] = self.api.title
    -        self._ddict["body"] = self.api.description
    -        self._ddict["number"] = self.api.number
    -
    -    @property
    -    def title(self):
    -        return self._ddict["title"]
    -
    -    @title.setter
    -    def title(self, val):
    -        self._ddict["title"] = val
    -        self.api.edit(title=val)
    -
    -    @property
    -    def ddict(self):
    -        if not self._ddict:
    -            # no dict yet, fetch from github
    -            self.load()
    -        return self._ddict
    -
    -
    -
    -    # synonym to let the tags of super class work
    -    @property
    -    def body(self):
    -        return self._ddict["body"]
    -
    -    @body.setter
    -    def body(self, val):
    -        if self._ddict["body"] != val:
    -            self._ddict["body"] = val
    -            self.api.edit(self.title, description=val)
    -
    -    # @property
    -    # def deadline(self):
    -    #     return self._ddict["deadline"]
    -
    -    # @deadline.setter
    -    # def deadline(self, val):
    -    #     #due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(val)))
    -
    -    #     self._ddict["deadline"] = val
    -    #     self.api.edit(title=self.title)
    -
    -    @property
    -    def id(self):
    -        return self._ddict["id"]
    -
    -    @property
    -    def url(self):
    -        return self._ddict["url"]
    -
    -    @property
    -    def number(self):
    -        return self._ddict["number"]
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var body
    -
    -
    -
    -Source code -
    @property
    -def body(self):
    -    return self._ddict["body"]
    -
    -
    -
    var ddict
    -
    -
    -
    -Source code -
    @property
    -def ddict(self):
    -    if not self._ddict:
    -        # no dict yet, fetch from github
    -        self.load()
    -    return self._ddict
    -
    -
    -
    var id
    -
    -
    -
    -Source code -
    @property
    -def id(self):
    -    return self._ddict["id"]
    -
    -
    -
    var number
    -
    -
    -
    -Source code -
    @property
    -def number(self):
    -    return self._ddict["number"]
    -
    -
    -
    var title
    -
    -
    -
    -Source code -
    @property
    -def title(self):
    -    return self._ddict["title"]
    -
    -
    -
    var url
    -
    -
    -
    -Source code -
    @property
    -def url(self):
    -    return self._ddict["url"]
    -
    -
    -
    -

    Methods

    -
    -
    -def load(self) -
    -
    -
    -
    -Source code -
    def load(self):
    -    self._ddict = {}
    -    #self._ddict["deadline"] = j.data.time.any2HRDateTime(self.api.due_on)
    -    self._ddict["id"] = self.api.id
    -    self._ddict["url"] = self.api.url
    -    self._ddict["title"] = self.api.title
    -    self._ddict["body"] = self.api.description
    -    self._ddict["number"] = self.api.number
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/github/repo.html b/docs/api/jumpscale/clients/github/repo.html deleted file mode 100644 index 958542a1f..000000000 --- a/docs/api/jumpscale/clients/github/repo.html +++ /dev/null @@ -1,1525 +0,0 @@ - - - - - - -jumpscale.clients.github.repo API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.github.repo

    -
    -
    -
    -Source code -
    import threading
    -import copy
    -import base64
    -from .issue import Issue
    -
    -from .base import replacelabels
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import Base, fields
    -from jumpscale.loader import j
    -
    -# import collections
    -import urllib
    -from .milestone import RepoMilestone
    -from github.GithubException import UnknownObjectException
    -
    -
    -class GithubRepo:
    -    TYPES = ["story", "ticket", "task", "bug", "feature", "question", "monitor", "unknown"]
    -    PRIORITIES = ["critical", "urgent", "normal", "minor"]
    -
    -    STATES = ["new", "accepted", "question", "inprogress", "verification", "closed"]
    -
    -    def __init__(self, client, fullname):
    -        self.client = client
    -        self.fullname = fullname
    -        self._repoclient = None
    -        self._labels = None
    -        self._issues = None
    -        self._lock = threading.RLock()
    -        self._milestones = None
    -
    -    def _log_info(self, s):
    -        pass
    -
    -    @property
    -    def api(self):
    -        if self._repoclient is None:
    -            self._repoclient = self.client.get_repo(self.fullname)
    -        return self._repoclient
    -
    -    @property
    -    def name(self):
    -        return self.fullname.split("/", 1)[-1]
    -
    -    @property
    -    def type(self):
    -        if self.name in ["home"]:
    -            return "home"
    -        elif self.name.startswith("proj"):
    -            return "proj"
    -        elif self.name.startswith("org_"):
    -            return "org"
    -        elif self.name.startswith("www"):
    -            return "www"
    -        elif self.name.startswith("doc"):
    -            return "doc"
    -        elif self.name.startswith("cockpit"):
    -            return "cockpit"
    -        else:
    -            return "code"
    -
    -    @property
    -    def labelnames(self):
    -        return [item.name for item in self.labels]
    -
    -    @property
    -    def labels(self):
    -        with self._lock:
    -            if self._labels is None:
    -                self._labels = [item for item in self.api.get_labels()]
    -
    -        return self._labels
    -
    -    @property
    -    def stories(self):
    -        # walk overall issues find the stories (based on type)
    -        # only for home type repo, otherwise return []
    -        return self.issues_by_type("story")
    -
    -    @property
    -    def tasks(self):
    -        # walk overall issues find the stories (based on type)
    -        # only for home type repo, otherwise return []
    -        return self.issues_by_type("task")
    -
    -    def labelsSet(self, labels2set, ignoreDelete=["p_"], delete=True):
    -        """
    -        @param ignore all labels starting with ignore will not be deleted
    -        """
    -
    -        for item in labels2set:
    -            if not isinstance(item, str):
    -                raise Exception("Labels to set need to be in string format, found:%s" % labels2set)
    -
    -        # walk over github existing labels
    -        labelstowalk = copy.copy(self.labels)
    -        for item in labelstowalk:
    -            name = item.name.lower()
    -            if name not in labels2set:
    -                # label in repo does not correspond to label we need
    -                if name in replacelabels:
    -                    nameNew = replacelabels[item.name.lower()]
    -                    if nameNew not in self.labelnames:
    -                        color = self.get_color(name)
    -                        self._log_info(
    -                            "change label in repo: %s oldlabel:'%s' to:'%s' color:%s"
    -                            % (self.fullname, item.name, nameNew, color)
    -                        )
    -                        item.edit(nameNew, color)
    -                        self._labels = None
    -                else:
    -                    # no replacement
    -                    name = "type_unknown"
    -                    color = self.get_color(name)
    -                    try:
    -                        item.edit(name, color)
    -                    except BaseException:
    -                        item.delete()
    -                    self._labels = None
    -
    -        # walk over new labels we need to set
    -        for name in labels2set:
    -            if name not in self.labelnames:
    -                # does not exist yet in repo
    -                color = self.get_color(name)
    -                self._log_info("create label: %s %s %s" % (self.fullname, name, color))
    -                self.api.create_label(name, color)
    -                self._labels = None
    -
    -        name = ""
    -
    -        if delete:
    -            labelstowalk = copy.copy(self.labels)
    -            for item in labelstowalk:
    -                if item.name not in labels2set:
    -                    self._log_info("delete label: %s %s" % (self.fullname, item.name))
    -                    ignoreDeleteDo = False
    -                    for filteritem in ignoreDelete:
    -                        if item.name.startswith(filteritem):
    -                            ignoreDeleteDo = True
    -                    if ignoreDeleteDo is False:
    -                        item.delete()
    -                    self._labels = None
    -
    -        # check the colors
    -        labelstowalk = copy.copy(self.labels)
    -        for item in labelstowalk:
    -            # we recognise the label
    -            self._log_info("check color of repo:%s labelname:'%s'" % (self.fullname, item.name))
    -            color = self.get_color(item.name)
    -            if item.color != color:
    -                self._log_info("change label color for repo %s %s" % (item.name, color))
    -                item.edit(item.name, color)
    -                self._labels = None
    -
    -    def getlabel(self, name):
    -        for item in self.labels:
    -            self._log_info("%s:look for name:'%s'" % (item.name, name))
    -            if item.name == name:
    -                return item
    -        raise Exception("Dit not find label: '%s'" % name)
    -
    -    def get_issue_from_markdown(self, issueNumber, markdown):
    -        i = self.get_issue(issueNumber, False)
    -        i._loadMD(markdown)
    -        self.issues.append(i)
    -        return i
    -
    -    def get_issue(self, issueNumber, die=True):
    -        for issue in self.issues:
    -            if issue.number == issueNumber:
    -                return issue
    -        # not found in cache, try to load from github
    -        github_issue = self.api.get_issue(issueNumber)
    -
    -        if github_issue:
    -            issue = Issue(repo=self, githubObj=github_issue)
    -            self._issues.append(issue)
    -            return issue
    -
    -        if die:
    -            raise Exception("cannot find issue:%s in repo:%s" % (issueNumber, self))
    -        else:
    -            i = Issue(self)
    -            i._ddict["number"] = issueNumber
    -            return i
    -
    -    def issues_by_type(self, *types):
    -        """
    -        filter is method which takes  issue as argument and returns True or False to include
    -        """
    -        issues = []
    -        for issue in self.issues:
    -            if issue.type in types:
    -                issues.append(issue)
    -
    -        return issues
    -
    -    def issues_by_state(self, filter=None):
    -        """
    -        filter is method which takes  issue as argument and returns True or False to include
    -        """
    -        res = {}
    -        for item in self.states:
    -            res[item] = []
    -            for issue in self.issues:
    -                if issue.state == item:
    -                    if filter is None or filter(issue):
    -                        res[item].append(issue)
    -        return res
    -
    -    def issues_by_priority(self, filter=None):
    -        """
    -        filter is method which takes  issue as argument and returns True or False to include
    -        """
    -        res = {}
    -        for item in self.priorities:
    -            res[item] = []
    -            for issue in self.issues:
    -                if issue.priority == item:
    -                    if filter is None or filter(issue):
    -                        res[item].append(issue)
    -        return res
    -
    -    def issues_by_type_state(self, filter=None, collapsepriority=True):
    -        """
    -        filter is method which takes  issue as argument and returns True or False to include
    -        returns dict of dict keys: type, state and then issues sorted following priority
    -        """
    -        res = {}
    -        for type in self.types:
    -            res[type] = {}
    -            for state in self.states:
    -                res[type][state] = {}
    -                for priority in self.priorities:
    -                    res[type][state][priority] = []
    -                    for issue in self.issues:
    -                        if issue.type == type and issue.state == state:
    -                            if filter is None or filter(issue):
    -                                res[type][state][priority].append(issue)
    -                if collapsepriority:
    -                    # sort the issues following priority
    -                    temp = res[type][state]
    -                    res[type][state] = []
    -                    for priority in self.priorities:
    -                        for subitem in temp[priority]:
    -                            res[type][state].append(subitem)
    -        return res
    -
    -    @property
    -    def types(self):
    -        return GithubRepo.TYPES
    -
    -    @property
    -    def priorities(self):
    -        return GithubRepo.PRIORITIES
    -
    -    @property
    -    def states(self):
    -        return GithubRepo.STATES
    -
    -    @property
    -    def milestones(self):
    -        if self._milestones is None:
    -            self._milestones = [RepoMilestone(self, x) for x in self.api.get_milestones()]
    -
    -        return self._milestones
    -
    -    @property
    -    def milestone_titles(self):
    -        return [item.title for item in self.milestones]
    -
    -    @property
    -    def milestone_names(self):
    -        return [item.name for item in self.milestones]
    -
    -    def get_milestone(self, name, die=True):
    -        name = name.strip()
    -        if name == "":
    -            raise Exception("Name cannot be empty.")
    -        for item in self.milestones:
    -            if name == item.name.strip() or name == item.title.strip():
    -                return item
    -        if die:
    -            raise Exception("Could not find milestone with name:%s" % name)
    -        else:
    -            return None
    -
    -    def create_milestone(self, name, title, description="", deadline="", owner=""):
    -        self._log_info('Attempt to create milestone "%s" [%s] deadline %s' % (name, title, deadline))
    -
    -        def getBody(descr, name, owner):
    -            out = "%s\n\n" % descr
    -            out += "## name:%s\n" % name
    -            out += "## owner:%s\n" % owner
    -            return out
    -
    -        ms = None
    -        for s in [name, title]:
    -            ms = self.get_milestone(s, die=False)
    -            if ms is not None:
    -                break
    -
    -        if ms is not None:
    -            if ms.title != title:
    -                ms.title = title
    -            # if ms.deadline != deadline:
    -            #     ms.deadline = deadline
    -            tocheck = getBody(description.strip(), name, owner)
    -            if ms.body.strip() != tocheck.strip():
    -                ms.body = tocheck
    -        else:
    -            # due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(deadline)))
    -            self._log_info("Create milestone on %s: %s" % (self, title))
    -            body = getBody(description.strip(), name, owner)
    -            # workaround for https://github.com/PyGithub/PyGithub/issues/396
    -            milestone = self.api.create_milestone(title=title, description=body)
    -            milestone.edit(title=title)
    -
    -            self._milestones.append(RepoMilestone(self, milestone))
    -
    -    def delete_milestone(self, name):
    -        if name.strip() == "":
    -            raise Exception("Name cannot be empty.")
    -        self._log_info("Delete milestone on %s: '%s'" % (self, name))
    -        try:
    -            ms = self.get_milestone(name)
    -            ms.api.delete()
    -            self._milestones = []
    -        except Exception:
    -            self._log_info("Milestone '%s' doesn't exist. no need to delete" % name)
    -
    -    def _labelsubset(self, cat):
    -        res = []
    -        for item in self.labels:
    -            if item.startswith(cat):
    -                item = item[len(cat) :].strip("_")
    -                res.append(item)
    -        res.sort()
    -        return res
    -
    -    def get_color(self, name):
    -
    -        # colors={'state_question':'fbca04',
    -        #  'priority_urgent':'d93f0b',
    -        #  'state_verification':'006b75',
    -        #  'priority_minor':'',
    -        #  'type_task':'',
    -        #  'type_feature':'',
    -        #  'process_wontfix':"ffffff",
    -        #  'priority_critical':"b60205",
    -        #  'state_inprogress':"e6e6e6",
    -        #  'priority_normal':"e6e6e6",
    -        #  'type_story':"ee9a00",
    -        #  'process_duplicate':"",
    -        #  'state_closed':"5319e7",
    -        #  'type_bug':"fc2929",
    -        #  'state_accepted':"0e8a16",
    -        #  'type_question':"fbca04",
    -        #  'state_new':"1d76db"}
    -
    -        if name.startswith("state"):
    -            return "c2e0c6"  # light green
    -
    -        if name.startswith("process"):
    -            return "d4c5f9"  # light purple
    -
    -        if name.startswith("type"):
    -            return "fef2c0"  # light yellow
    -
    -        if name in ("priority_critical", "task_no_estimation"):
    -            return "b60205"  # red
    -
    -        if name.startswith("priority_urgent"):
    -            return "d93f0b"
    -
    -        if name.startswith("priority"):
    -            return "f9d0c4"  # roze
    -
    -        return "ffffff"
    -
    -    def set_file(self, path, content, message="update file"):
    -        """
    -        Creates or updates the file content at path with given content
    -        :param path: file path `README.md`
    -        :param content: Plain content of file
    -        :return:
    -        """
    -        bytes = content.encode()
    -        encoded = base64.encodebytes(bytes)
    -
    -        params = {"message": message, "content": encoded.decode()}
    -
    -        path = urllib.parse.quote(path)
    -        try:
    -            obj = self.api.get_contents(path)
    -            params["sha"] = obj.sha
    -            if base64.decodebytes(obj.content.encode()) == bytes:
    -                return
    -        except UnknownObjectException:
    -            pass
    -
    -        self._log_info('Updating file "%s"' % path)
    -        self.api._requester.requestJsonAndCheck("PUT", self.api.url + "/contents/" + path, input=params)
    -
    -    @property
    -    def issues(self):
    -        with self._lock:
    -            if self._issues is None:
    -                issues = []
    -                for item in self.api.get_issues(state="all"):
    -                    issues.append(Issue(self, githubObj=item))
    -
    -                self._issues = issues
    -
    -        return self._issues
    -
    -    def __str__(self):
    -        return "gitrepo:%s" % self.fullname
    -
    -    __repr__ = __str__
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class GithubRepo -(client, fullname) -
    -
    -
    -
    -Source code -
    class GithubRepo:
    -    TYPES = ["story", "ticket", "task", "bug", "feature", "question", "monitor", "unknown"]
    -    PRIORITIES = ["critical", "urgent", "normal", "minor"]
    -
    -    STATES = ["new", "accepted", "question", "inprogress", "verification", "closed"]
    -
    -    def __init__(self, client, fullname):
    -        self.client = client
    -        self.fullname = fullname
    -        self._repoclient = None
    -        self._labels = None
    -        self._issues = None
    -        self._lock = threading.RLock()
    -        self._milestones = None
    -
    -    def _log_info(self, s):
    -        pass
    -
    -    @property
    -    def api(self):
    -        if self._repoclient is None:
    -            self._repoclient = self.client.get_repo(self.fullname)
    -        return self._repoclient
    -
    -    @property
    -    def name(self):
    -        return self.fullname.split("/", 1)[-1]
    -
    -    @property
    -    def type(self):
    -        if self.name in ["home"]:
    -            return "home"
    -        elif self.name.startswith("proj"):
    -            return "proj"
    -        elif self.name.startswith("org_"):
    -            return "org"
    -        elif self.name.startswith("www"):
    -            return "www"
    -        elif self.name.startswith("doc"):
    -            return "doc"
    -        elif self.name.startswith("cockpit"):
    -            return "cockpit"
    -        else:
    -            return "code"
    -
    -    @property
    -    def labelnames(self):
    -        return [item.name for item in self.labels]
    -
    -    @property
    -    def labels(self):
    -        with self._lock:
    -            if self._labels is None:
    -                self._labels = [item for item in self.api.get_labels()]
    -
    -        return self._labels
    -
    -    @property
    -    def stories(self):
    -        # walk overall issues find the stories (based on type)
    -        # only for home type repo, otherwise return []
    -        return self.issues_by_type("story")
    -
    -    @property
    -    def tasks(self):
    -        # walk overall issues find the stories (based on type)
    -        # only for home type repo, otherwise return []
    -        return self.issues_by_type("task")
    -
    -    def labelsSet(self, labels2set, ignoreDelete=["p_"], delete=True):
    -        """
    -        @param ignore all labels starting with ignore will not be deleted
    -        """
    -
    -        for item in labels2set:
    -            if not isinstance(item, str):
    -                raise Exception("Labels to set need to be in string format, found:%s" % labels2set)
    -
    -        # walk over github existing labels
    -        labelstowalk = copy.copy(self.labels)
    -        for item in labelstowalk:
    -            name = item.name.lower()
    -            if name not in labels2set:
    -                # label in repo does not correspond to label we need
    -                if name in replacelabels:
    -                    nameNew = replacelabels[item.name.lower()]
    -                    if nameNew not in self.labelnames:
    -                        color = self.get_color(name)
    -                        self._log_info(
    -                            "change label in repo: %s oldlabel:'%s' to:'%s' color:%s"
    -                            % (self.fullname, item.name, nameNew, color)
    -                        )
    -                        item.edit(nameNew, color)
    -                        self._labels = None
    -                else:
    -                    # no replacement
    -                    name = "type_unknown"
    -                    color = self.get_color(name)
    -                    try:
    -                        item.edit(name, color)
    -                    except BaseException:
    -                        item.delete()
    -                    self._labels = None
    -
    -        # walk over new labels we need to set
    -        for name in labels2set:
    -            if name not in self.labelnames:
    -                # does not exist yet in repo
    -                color = self.get_color(name)
    -                self._log_info("create label: %s %s %s" % (self.fullname, name, color))
    -                self.api.create_label(name, color)
    -                self._labels = None
    -
    -        name = ""
    -
    -        if delete:
    -            labelstowalk = copy.copy(self.labels)
    -            for item in labelstowalk:
    -                if item.name not in labels2set:
    -                    self._log_info("delete label: %s %s" % (self.fullname, item.name))
    -                    ignoreDeleteDo = False
    -                    for filteritem in ignoreDelete:
    -                        if item.name.startswith(filteritem):
    -                            ignoreDeleteDo = True
    -                    if ignoreDeleteDo is False:
    -                        item.delete()
    -                    self._labels = None
    -
    -        # check the colors
    -        labelstowalk = copy.copy(self.labels)
    -        for item in labelstowalk:
    -            # we recognise the label
    -            self._log_info("check color of repo:%s labelname:'%s'" % (self.fullname, item.name))
    -            color = self.get_color(item.name)
    -            if item.color != color:
    -                self._log_info("change label color for repo %s %s" % (item.name, color))
    -                item.edit(item.name, color)
    -                self._labels = None
    -
    -    def getlabel(self, name):
    -        for item in self.labels:
    -            self._log_info("%s:look for name:'%s'" % (item.name, name))
    -            if item.name == name:
    -                return item
    -        raise Exception("Dit not find label: '%s'" % name)
    -
    -    def get_issue_from_markdown(self, issueNumber, markdown):
    -        i = self.get_issue(issueNumber, False)
    -        i._loadMD(markdown)
    -        self.issues.append(i)
    -        return i
    -
    -    def get_issue(self, issueNumber, die=True):
    -        for issue in self.issues:
    -            if issue.number == issueNumber:
    -                return issue
    -        # not found in cache, try to load from github
    -        github_issue = self.api.get_issue(issueNumber)
    -
    -        if github_issue:
    -            issue = Issue(repo=self, githubObj=github_issue)
    -            self._issues.append(issue)
    -            return issue
    -
    -        if die:
    -            raise Exception("cannot find issue:%s in repo:%s" % (issueNumber, self))
    -        else:
    -            i = Issue(self)
    -            i._ddict["number"] = issueNumber
    -            return i
    -
    -    def issues_by_type(self, *types):
    -        """
    -        filter is method which takes  issue as argument and returns True or False to include
    -        """
    -        issues = []
    -        for issue in self.issues:
    -            if issue.type in types:
    -                issues.append(issue)
    -
    -        return issues
    -
    -    def issues_by_state(self, filter=None):
    -        """
    -        filter is method which takes  issue as argument and returns True or False to include
    -        """
    -        res = {}
    -        for item in self.states:
    -            res[item] = []
    -            for issue in self.issues:
    -                if issue.state == item:
    -                    if filter is None or filter(issue):
    -                        res[item].append(issue)
    -        return res
    -
    -    def issues_by_priority(self, filter=None):
    -        """
    -        filter is method which takes  issue as argument and returns True or False to include
    -        """
    -        res = {}
    -        for item in self.priorities:
    -            res[item] = []
    -            for issue in self.issues:
    -                if issue.priority == item:
    -                    if filter is None or filter(issue):
    -                        res[item].append(issue)
    -        return res
    -
    -    def issues_by_type_state(self, filter=None, collapsepriority=True):
    -        """
    -        filter is method which takes  issue as argument and returns True or False to include
    -        returns dict of dict keys: type, state and then issues sorted following priority
    -        """
    -        res = {}
    -        for type in self.types:
    -            res[type] = {}
    -            for state in self.states:
    -                res[type][state] = {}
    -                for priority in self.priorities:
    -                    res[type][state][priority] = []
    -                    for issue in self.issues:
    -                        if issue.type == type and issue.state == state:
    -                            if filter is None or filter(issue):
    -                                res[type][state][priority].append(issue)
    -                if collapsepriority:
    -                    # sort the issues following priority
    -                    temp = res[type][state]
    -                    res[type][state] = []
    -                    for priority in self.priorities:
    -                        for subitem in temp[priority]:
    -                            res[type][state].append(subitem)
    -        return res
    -
    -    @property
    -    def types(self):
    -        return GithubRepo.TYPES
    -
    -    @property
    -    def priorities(self):
    -        return GithubRepo.PRIORITIES
    -
    -    @property
    -    def states(self):
    -        return GithubRepo.STATES
    -
    -    @property
    -    def milestones(self):
    -        if self._milestones is None:
    -            self._milestones = [RepoMilestone(self, x) for x in self.api.get_milestones()]
    -
    -        return self._milestones
    -
    -    @property
    -    def milestone_titles(self):
    -        return [item.title for item in self.milestones]
    -
    -    @property
    -    def milestone_names(self):
    -        return [item.name for item in self.milestones]
    -
    -    def get_milestone(self, name, die=True):
    -        name = name.strip()
    -        if name == "":
    -            raise Exception("Name cannot be empty.")
    -        for item in self.milestones:
    -            if name == item.name.strip() or name == item.title.strip():
    -                return item
    -        if die:
    -            raise Exception("Could not find milestone with name:%s" % name)
    -        else:
    -            return None
    -
    -    def create_milestone(self, name, title, description="", deadline="", owner=""):
    -        self._log_info('Attempt to create milestone "%s" [%s] deadline %s' % (name, title, deadline))
    -
    -        def getBody(descr, name, owner):
    -            out = "%s\n\n" % descr
    -            out += "## name:%s\n" % name
    -            out += "## owner:%s\n" % owner
    -            return out
    -
    -        ms = None
    -        for s in [name, title]:
    -            ms = self.get_milestone(s, die=False)
    -            if ms is not None:
    -                break
    -
    -        if ms is not None:
    -            if ms.title != title:
    -                ms.title = title
    -            # if ms.deadline != deadline:
    -            #     ms.deadline = deadline
    -            tocheck = getBody(description.strip(), name, owner)
    -            if ms.body.strip() != tocheck.strip():
    -                ms.body = tocheck
    -        else:
    -            # due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(deadline)))
    -            self._log_info("Create milestone on %s: %s" % (self, title))
    -            body = getBody(description.strip(), name, owner)
    -            # workaround for https://github.com/PyGithub/PyGithub/issues/396
    -            milestone = self.api.create_milestone(title=title, description=body)
    -            milestone.edit(title=title)
    -
    -            self._milestones.append(RepoMilestone(self, milestone))
    -
    -    def delete_milestone(self, name):
    -        if name.strip() == "":
    -            raise Exception("Name cannot be empty.")
    -        self._log_info("Delete milestone on %s: '%s'" % (self, name))
    -        try:
    -            ms = self.get_milestone(name)
    -            ms.api.delete()
    -            self._milestones = []
    -        except Exception:
    -            self._log_info("Milestone '%s' doesn't exist. no need to delete" % name)
    -
    -    def _labelsubset(self, cat):
    -        res = []
    -        for item in self.labels:
    -            if item.startswith(cat):
    -                item = item[len(cat) :].strip("_")
    -                res.append(item)
    -        res.sort()
    -        return res
    -
    -    def get_color(self, name):
    -
    -        # colors={'state_question':'fbca04',
    -        #  'priority_urgent':'d93f0b',
    -        #  'state_verification':'006b75',
    -        #  'priority_minor':'',
    -        #  'type_task':'',
    -        #  'type_feature':'',
    -        #  'process_wontfix':"ffffff",
    -        #  'priority_critical':"b60205",
    -        #  'state_inprogress':"e6e6e6",
    -        #  'priority_normal':"e6e6e6",
    -        #  'type_story':"ee9a00",
    -        #  'process_duplicate':"",
    -        #  'state_closed':"5319e7",
    -        #  'type_bug':"fc2929",
    -        #  'state_accepted':"0e8a16",
    -        #  'type_question':"fbca04",
    -        #  'state_new':"1d76db"}
    -
    -        if name.startswith("state"):
    -            return "c2e0c6"  # light green
    -
    -        if name.startswith("process"):
    -            return "d4c5f9"  # light purple
    -
    -        if name.startswith("type"):
    -            return "fef2c0"  # light yellow
    -
    -        if name in ("priority_critical", "task_no_estimation"):
    -            return "b60205"  # red
    -
    -        if name.startswith("priority_urgent"):
    -            return "d93f0b"
    -
    -        if name.startswith("priority"):
    -            return "f9d0c4"  # roze
    -
    -        return "ffffff"
    -
    -    def set_file(self, path, content, message="update file"):
    -        """
    -        Creates or updates the file content at path with given content
    -        :param path: file path `README.md`
    -        :param content: Plain content of file
    -        :return:
    -        """
    -        bytes = content.encode()
    -        encoded = base64.encodebytes(bytes)
    -
    -        params = {"message": message, "content": encoded.decode()}
    -
    -        path = urllib.parse.quote(path)
    -        try:
    -            obj = self.api.get_contents(path)
    -            params["sha"] = obj.sha
    -            if base64.decodebytes(obj.content.encode()) == bytes:
    -                return
    -        except UnknownObjectException:
    -            pass
    -
    -        self._log_info('Updating file "%s"' % path)
    -        self.api._requester.requestJsonAndCheck("PUT", self.api.url + "/contents/" + path, input=params)
    -
    -    @property
    -    def issues(self):
    -        with self._lock:
    -            if self._issues is None:
    -                issues = []
    -                for item in self.api.get_issues(state="all"):
    -                    issues.append(Issue(self, githubObj=item))
    -
    -                self._issues = issues
    -
    -        return self._issues
    -
    -    def __str__(self):
    -        return "gitrepo:%s" % self.fullname
    -
    -    __repr__ = __str__
    -
    -

    Class variables

    -
    -
    var PRIORITIES
    -
    -
    -
    -
    var STATES
    -
    -
    -
    -
    var TYPES
    -
    -
    -
    -
    -

    Instance variables

    -
    -
    var api
    -
    -
    -
    -Source code -
    @property
    -def api(self):
    -    if self._repoclient is None:
    -        self._repoclient = self.client.get_repo(self.fullname)
    -    return self._repoclient
    -
    -
    -
    var issues
    -
    -
    -
    -Source code -
    @property
    -def issues(self):
    -    with self._lock:
    -        if self._issues is None:
    -            issues = []
    -            for item in self.api.get_issues(state="all"):
    -                issues.append(Issue(self, githubObj=item))
    -
    -            self._issues = issues
    -
    -    return self._issues
    -
    -
    -
    var labelnames
    -
    -
    -
    -Source code -
    @property
    -def labelnames(self):
    -    return [item.name for item in self.labels]
    -
    -
    -
    var labels
    -
    -
    -
    -Source code -
    @property
    -def labels(self):
    -    with self._lock:
    -        if self._labels is None:
    -            self._labels = [item for item in self.api.get_labels()]
    -
    -    return self._labels
    -
    -
    -
    var milestone_names
    -
    -
    -
    -Source code -
    @property
    -def milestone_names(self):
    -    return [item.name for item in self.milestones]
    -
    -
    -
    var milestone_titles
    -
    -
    -
    -Source code -
    @property
    -def milestone_titles(self):
    -    return [item.title for item in self.milestones]
    -
    -
    -
    var milestones
    -
    -
    -
    -Source code -
    @property
    -def milestones(self):
    -    if self._milestones is None:
    -        self._milestones = [RepoMilestone(self, x) for x in self.api.get_milestones()]
    -
    -    return self._milestones
    -
    -
    -
    var name
    -
    -
    -
    -Source code -
    @property
    -def name(self):
    -    return self.fullname.split("/", 1)[-1]
    -
    -
    -
    var priorities
    -
    -
    -
    -Source code -
    @property
    -def priorities(self):
    -    return GithubRepo.PRIORITIES
    -
    -
    -
    var states
    -
    -
    -
    -Source code -
    @property
    -def states(self):
    -    return GithubRepo.STATES
    -
    -
    -
    var stories
    -
    -
    -
    -Source code -
    @property
    -def stories(self):
    -    # walk overall issues find the stories (based on type)
    -    # only for home type repo, otherwise return []
    -    return self.issues_by_type("story")
    -
    -
    -
    var tasks
    -
    -
    -
    -Source code -
    @property
    -def tasks(self):
    -    # walk overall issues find the stories (based on type)
    -    # only for home type repo, otherwise return []
    -    return self.issues_by_type("task")
    -
    -
    -
    var type
    -
    -
    -
    -Source code -
    @property
    -def type(self):
    -    if self.name in ["home"]:
    -        return "home"
    -    elif self.name.startswith("proj"):
    -        return "proj"
    -    elif self.name.startswith("org_"):
    -        return "org"
    -    elif self.name.startswith("www"):
    -        return "www"
    -    elif self.name.startswith("doc"):
    -        return "doc"
    -    elif self.name.startswith("cockpit"):
    -        return "cockpit"
    -    else:
    -        return "code"
    -
    -
    -
    var types
    -
    -
    -
    -Source code -
    @property
    -def types(self):
    -    return GithubRepo.TYPES
    -
    -
    -
    -

    Methods

    -
    -
    -def create_milestone(self, name, title, description='', deadline='', owner='') -
    -
    -
    -
    -Source code -
    def create_milestone(self, name, title, description="", deadline="", owner=""):
    -    self._log_info('Attempt to create milestone "%s" [%s] deadline %s' % (name, title, deadline))
    -
    -    def getBody(descr, name, owner):
    -        out = "%s\n\n" % descr
    -        out += "## name:%s\n" % name
    -        out += "## owner:%s\n" % owner
    -        return out
    -
    -    ms = None
    -    for s in [name, title]:
    -        ms = self.get_milestone(s, die=False)
    -        if ms is not None:
    -            break
    -
    -    if ms is not None:
    -        if ms.title != title:
    -            ms.title = title
    -        # if ms.deadline != deadline:
    -        #     ms.deadline = deadline
    -        tocheck = getBody(description.strip(), name, owner)
    -        if ms.body.strip() != tocheck.strip():
    -            ms.body = tocheck
    -    else:
    -        # due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(deadline)))
    -        self._log_info("Create milestone on %s: %s" % (self, title))
    -        body = getBody(description.strip(), name, owner)
    -        # workaround for https://github.com/PyGithub/PyGithub/issues/396
    -        milestone = self.api.create_milestone(title=title, description=body)
    -        milestone.edit(title=title)
    -
    -        self._milestones.append(RepoMilestone(self, milestone))
    -
    -
    -
    -def delete_milestone(self, name) -
    -
    -
    -
    -Source code -
    def delete_milestone(self, name):
    -    if name.strip() == "":
    -        raise Exception("Name cannot be empty.")
    -    self._log_info("Delete milestone on %s: '%s'" % (self, name))
    -    try:
    -        ms = self.get_milestone(name)
    -        ms.api.delete()
    -        self._milestones = []
    -    except Exception:
    -        self._log_info("Milestone '%s' doesn't exist. no need to delete" % name)
    -
    -
    -
    -def get_color(self, name) -
    -
    -
    -
    -Source code -
    def get_color(self, name):
    -
    -    # colors={'state_question':'fbca04',
    -    #  'priority_urgent':'d93f0b',
    -    #  'state_verification':'006b75',
    -    #  'priority_minor':'',
    -    #  'type_task':'',
    -    #  'type_feature':'',
    -    #  'process_wontfix':"ffffff",
    -    #  'priority_critical':"b60205",
    -    #  'state_inprogress':"e6e6e6",
    -    #  'priority_normal':"e6e6e6",
    -    #  'type_story':"ee9a00",
    -    #  'process_duplicate':"",
    -    #  'state_closed':"5319e7",
    -    #  'type_bug':"fc2929",
    -    #  'state_accepted':"0e8a16",
    -    #  'type_question':"fbca04",
    -    #  'state_new':"1d76db"}
    -
    -    if name.startswith("state"):
    -        return "c2e0c6"  # light green
    -
    -    if name.startswith("process"):
    -        return "d4c5f9"  # light purple
    -
    -    if name.startswith("type"):
    -        return "fef2c0"  # light yellow
    -
    -    if name in ("priority_critical", "task_no_estimation"):
    -        return "b60205"  # red
    -
    -    if name.startswith("priority_urgent"):
    -        return "d93f0b"
    -
    -    if name.startswith("priority"):
    -        return "f9d0c4"  # roze
    -
    -    return "ffffff"
    -
    -
    -
    -def get_issue(self, issueNumber, die=True) -
    -
    -
    -
    -Source code -
    def get_issue(self, issueNumber, die=True):
    -    for issue in self.issues:
    -        if issue.number == issueNumber:
    -            return issue
    -    # not found in cache, try to load from github
    -    github_issue = self.api.get_issue(issueNumber)
    -
    -    if github_issue:
    -        issue = Issue(repo=self, githubObj=github_issue)
    -        self._issues.append(issue)
    -        return issue
    -
    -    if die:
    -        raise Exception("cannot find issue:%s in repo:%s" % (issueNumber, self))
    -    else:
    -        i = Issue(self)
    -        i._ddict["number"] = issueNumber
    -        return i
    -
    -
    -
    -def get_issue_from_markdown(self, issueNumber, markdown) -
    -
    -
    -
    -Source code -
    def get_issue_from_markdown(self, issueNumber, markdown):
    -    i = self.get_issue(issueNumber, False)
    -    i._loadMD(markdown)
    -    self.issues.append(i)
    -    return i
    -
    -
    -
    -def get_milestone(self, name, die=True) -
    -
    -
    -
    -Source code -
    def get_milestone(self, name, die=True):
    -    name = name.strip()
    -    if name == "":
    -        raise Exception("Name cannot be empty.")
    -    for item in self.milestones:
    -        if name == item.name.strip() or name == item.title.strip():
    -            return item
    -    if die:
    -        raise Exception("Could not find milestone with name:%s" % name)
    -    else:
    -        return None
    -
    -
    -
    -def getlabel(self, name) -
    -
    -
    -
    -Source code -
    def getlabel(self, name):
    -    for item in self.labels:
    -        self._log_info("%s:look for name:'%s'" % (item.name, name))
    -        if item.name == name:
    -            return item
    -    raise Exception("Dit not find label: '%s'" % name)
    -
    -
    -
    -def issues_by_priority(self, filter=None) -
    -
    -

    filter is method which takes -issue as argument and returns True or False to include

    -
    -Source code -
    def issues_by_priority(self, filter=None):
    -    """
    -    filter is method which takes  issue as argument and returns True or False to include
    -    """
    -    res = {}
    -    for item in self.priorities:
    -        res[item] = []
    -        for issue in self.issues:
    -            if issue.priority == item:
    -                if filter is None or filter(issue):
    -                    res[item].append(issue)
    -    return res
    -
    -
    -
    -def issues_by_state(self, filter=None) -
    -
    -

    filter is method which takes -issue as argument and returns True or False to include

    -
    -Source code -
    def issues_by_state(self, filter=None):
    -    """
    -    filter is method which takes  issue as argument and returns True or False to include
    -    """
    -    res = {}
    -    for item in self.states:
    -        res[item] = []
    -        for issue in self.issues:
    -            if issue.state == item:
    -                if filter is None or filter(issue):
    -                    res[item].append(issue)
    -    return res
    -
    -
    -
    -def issues_by_type(self, *types) -
    -
    -

    filter is method which takes -issue as argument and returns True or False to include

    -
    -Source code -
    def issues_by_type(self, *types):
    -    """
    -    filter is method which takes  issue as argument and returns True or False to include
    -    """
    -    issues = []
    -    for issue in self.issues:
    -        if issue.type in types:
    -            issues.append(issue)
    -
    -    return issues
    -
    -
    -
    -def issues_by_type_state(self, filter=None, collapsepriority=True) -
    -
    -

    filter is method which takes -issue as argument and returns True or False to include -returns dict of dict keys: type, state and then issues sorted following priority

    -
    -Source code -
    def issues_by_type_state(self, filter=None, collapsepriority=True):
    -    """
    -    filter is method which takes  issue as argument and returns True or False to include
    -    returns dict of dict keys: type, state and then issues sorted following priority
    -    """
    -    res = {}
    -    for type in self.types:
    -        res[type] = {}
    -        for state in self.states:
    -            res[type][state] = {}
    -            for priority in self.priorities:
    -                res[type][state][priority] = []
    -                for issue in self.issues:
    -                    if issue.type == type and issue.state == state:
    -                        if filter is None or filter(issue):
    -                            res[type][state][priority].append(issue)
    -            if collapsepriority:
    -                # sort the issues following priority
    -                temp = res[type][state]
    -                res[type][state] = []
    -                for priority in self.priorities:
    -                    for subitem in temp[priority]:
    -                        res[type][state].append(subitem)
    -    return res
    -
    -
    -
    -def labelsSet(self, labels2set, ignoreDelete=['p_'], delete=True) -
    -
    -

    @param ignore all labels starting with ignore will not be deleted

    -
    -Source code -
    def labelsSet(self, labels2set, ignoreDelete=["p_"], delete=True):
    -    """
    -    @param ignore all labels starting with ignore will not be deleted
    -    """
    -
    -    for item in labels2set:
    -        if not isinstance(item, str):
    -            raise Exception("Labels to set need to be in string format, found:%s" % labels2set)
    -
    -    # walk over github existing labels
    -    labelstowalk = copy.copy(self.labels)
    -    for item in labelstowalk:
    -        name = item.name.lower()
    -        if name not in labels2set:
    -            # label in repo does not correspond to label we need
    -            if name in replacelabels:
    -                nameNew = replacelabels[item.name.lower()]
    -                if nameNew not in self.labelnames:
    -                    color = self.get_color(name)
    -                    self._log_info(
    -                        "change label in repo: %s oldlabel:'%s' to:'%s' color:%s"
    -                        % (self.fullname, item.name, nameNew, color)
    -                    )
    -                    item.edit(nameNew, color)
    -                    self._labels = None
    -            else:
    -                # no replacement
    -                name = "type_unknown"
    -                color = self.get_color(name)
    -                try:
    -                    item.edit(name, color)
    -                except BaseException:
    -                    item.delete()
    -                self._labels = None
    -
    -    # walk over new labels we need to set
    -    for name in labels2set:
    -        if name not in self.labelnames:
    -            # does not exist yet in repo
    -            color = self.get_color(name)
    -            self._log_info("create label: %s %s %s" % (self.fullname, name, color))
    -            self.api.create_label(name, color)
    -            self._labels = None
    -
    -    name = ""
    -
    -    if delete:
    -        labelstowalk = copy.copy(self.labels)
    -        for item in labelstowalk:
    -            if item.name not in labels2set:
    -                self._log_info("delete label: %s %s" % (self.fullname, item.name))
    -                ignoreDeleteDo = False
    -                for filteritem in ignoreDelete:
    -                    if item.name.startswith(filteritem):
    -                        ignoreDeleteDo = True
    -                if ignoreDeleteDo is False:
    -                    item.delete()
    -                self._labels = None
    -
    -    # check the colors
    -    labelstowalk = copy.copy(self.labels)
    -    for item in labelstowalk:
    -        # we recognise the label
    -        self._log_info("check color of repo:%s labelname:'%s'" % (self.fullname, item.name))
    -        color = self.get_color(item.name)
    -        if item.color != color:
    -            self._log_info("change label color for repo %s %s" % (item.name, color))
    -            item.edit(item.name, color)
    -            self._labels = None
    -
    -
    -
    -def set_file(self, path, content, message='update file') -
    -
    -

    Creates or updates the file content at path with given content -:param path: file path README.md -:param content: Plain content of file -:return:

    -
    -Source code -
    def set_file(self, path, content, message="update file"):
    -    """
    -    Creates or updates the file content at path with given content
    -    :param path: file path `README.md`
    -    :param content: Plain content of file
    -    :return:
    -    """
    -    bytes = content.encode()
    -    encoded = base64.encodebytes(bytes)
    -
    -    params = {"message": message, "content": encoded.decode()}
    -
    -    path = urllib.parse.quote(path)
    -    try:
    -        obj = self.api.get_contents(path)
    -        params["sha"] = obj.sha
    -        if base64.decodebytes(obj.content.encode()) == bytes:
    -            return
    -    except UnknownObjectException:
    -        pass
    -
    -    self._log_info('Updating file "%s"' % path)
    -    self.api._requester.requestJsonAndCheck("PUT", self.api.url + "/contents/" + path, input=params)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/gogs/gogs.html b/docs/api/jumpscale/clients/gogs/gogs.html deleted file mode 100644 index a01571824..000000000 --- a/docs/api/jumpscale/clients/gogs/gogs.html +++ /dev/null @@ -1,323 +0,0 @@ - - - - - - -jumpscale.clients.gogs.gogs API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.gogs.gogs

    -
    -
    -
    -Source code -
    from jumpscale.clients.base import Base, Client
    -from jumpscale.core.base import fields
    -
    -
    -class User(Base):
    -    username = fields.String(required=True)
    -    password = fields.Secret(required=True)
    -
    -
    -class Gogs(Client):
    -    access_token = fields.Secret(required=True)
    -    user = fields.Object(User)
    -    admins = fields.List(fields.String(required=True), required=True)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Gogs -(parent_=None, instance_name_=None, **values) -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class Gogs(Client):
    -    access_token = fields.Secret(required=True)
    -    user = fields.Object(User)
    -    admins = fields.List(fields.String(required=True), required=True)
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var access_token
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var admins
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var user
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class User -(parent_=None, instance_name_=None, **values) -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class User(Base):
    -    username = fields.String(required=True)
    -    password = fields.Secret(required=True)
    -
    -

    Ancestors

    -
      -
    • Base
    • -
    • types.SimpleNamespace
    • -
    -

    Instance variables

    -
    -
    var password
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var username
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/gogs/index.html b/docs/api/jumpscale/clients/gogs/index.html deleted file mode 100644 index 53209f519..000000000 --- a/docs/api/jumpscale/clients/gogs/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -jumpscale.clients.gogs API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.gogs

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .gogs import Gogs
    -
    -    return StoredFactory(Gogs)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.gogs.gogs
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .gogs import Gogs
    -
    -    return StoredFactory(Gogs)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/mail/index.html b/docs/api/jumpscale/clients/mail/index.html deleted file mode 100644 index b4d989318..000000000 --- a/docs/api/jumpscale/clients/mail/index.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -jumpscale.clients.mail API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.mail

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .mail import MailClient
    -
    -    return StoredFactory(MailClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.mail.mail
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .mail import MailClient
    -
    -    return StoredFactory(MailClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/mail/mail.html b/docs/api/jumpscale/clients/mail/mail.html deleted file mode 100644 index cf6f2ccc6..000000000 --- a/docs/api/jumpscale/clients/mail/mail.html +++ /dev/null @@ -1,575 +0,0 @@ - - - - - - -jumpscale.clients.mail.mail API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.mail.mail

    -
    -
    -
    -Source code -
    from jumpscale.loader import j
    -import smtplib
    -import mimetypes
    -from email import encoders
    -from email.mime.audio import MIMEAudio
    -from email.mime.base import MIMEBase
    -from email.mime.image import MIMEImage
    -from email.mime.multipart import MIMEMultipart
    -from email.mime.text import MIMEText
    -
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -
    -class MailClient(Client):
    -    name = fields.String()
    -    smtp_server = fields.String()
    -    smtp_port = fields.Integer()
    -    login = fields.String()
    -    password = fields.String()
    -    sender_email = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -
    -    @property
    -    def is_ssl(self):
    -        self.smtp_port in [465, 587]
    -
    -    def send(self, recipients, sender="", subject="", message="", files=None, mimetype=None):
    -        """ Send an email to the recipients from the sender containing the message required and any attached files given by the paths in files
    -        :param recipients: Recipients of the message
    -        :type recipients: mixed, str or list
    -        :param sender: Sender of the email
    -        :type sender: str
    -        :param subject: Subject of the email
    -        :type subject: str
    -        :param message: Body of the email
    -        :type message: str
    -        :param files: List of paths to files to attach
    -        :type files: list of strings
    -        :param mimetype: Type of the body plain, html or None for autodetection
    -        :type mimetype: str
    -        """
    -        if not sender:
    -            sender = self.sender_email
    -        if isinstance(recipients, str):
    -            recipients = [recipients]
    -        server = smtplib.SMTP(self.smtp_server, self.smtp_port)
    -        server.ehlo()
    -        if self._ssl:
    -            server.starttls()
    -        if self.login:
    -            server.login(self.login, self.password)
    -
    -        if mimetype is None:
    -            if "<html>" in message:
    -                mimetype = "html"
    -            else:
    -                mimetype = "plain"
    -
    -        msg = MIMEText(message, mimetype)
    -
    -        msg["Subject"] = subject
    -        msg["From"] = sender
    -        msg["To"] = ",".join(recipients)
    -
    -        if files:
    -            txtmsg = msg
    -            msg = MIMEMultipart()
    -            msg["Subject"] = subject
    -            msg["From"] = sender
    -            msg["To"] = ",".join(recipients)
    -            msg.attach(txtmsg)
    -            for fl in files:
    -                # Guess the content type based on the file's extension.  Encoding
    -                # will be ignored, although we should check for simple things like
    -                # gzip'd or compressed files.
    -                filename = j.sals.fs.basename(fl)
    -                ctype, encoding = mimetypes.guess_type(fl)
    -                content = j.sals.fs.read_file(fl)
    -                if ctype is None or encoding is not None:
    -                    # No guess could be made, or the file is encoded (compressed), so
    -                    # use a generic bag-of-bits type.
    -                    ctype = "application/octet-stream"
    -                maintype, subtype = ctype.split("/", 1)
    -                if maintype == "text":
    -                    attachement = MIMEText(content, _subtype=subtype)
    -                elif maintype == "image":
    -                    attachement = MIMEImage(content, _subtype=subtype)
    -                elif maintype == "audio":
    -                    attachement = MIMEAudio(content, _subtype=subtype)
    -                else:
    -                    attachement = MIMEBase(maintype, subtype)
    -                    attachement.set_payload(content)
    -                    # Encode the payload using Base64
    -                    encoders.encode_base64(attachement)
    -                # Set the filename parameter
    -                attachement.add_header("Content-Disposition", "attachment", filename=filename)
    -                msg.attach(attachement)
    -        server.sendmail(sender, recipients, msg.as_string())
    -        server.close()
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class MailClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class MailClient(Client):
    -    name = fields.String()
    -    smtp_server = fields.String()
    -    smtp_port = fields.Integer()
    -    login = fields.String()
    -    password = fields.String()
    -    sender_email = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -
    -    @property
    -    def is_ssl(self):
    -        self.smtp_port in [465, 587]
    -
    -    def send(self, recipients, sender="", subject="", message="", files=None, mimetype=None):
    -        """ Send an email to the recipients from the sender containing the message required and any attached files given by the paths in files
    -        :param recipients: Recipients of the message
    -        :type recipients: mixed, str or list
    -        :param sender: Sender of the email
    -        :type sender: str
    -        :param subject: Subject of the email
    -        :type subject: str
    -        :param message: Body of the email
    -        :type message: str
    -        :param files: List of paths to files to attach
    -        :type files: list of strings
    -        :param mimetype: Type of the body plain, html or None for autodetection
    -        :type mimetype: str
    -        """
    -        if not sender:
    -            sender = self.sender_email
    -        if isinstance(recipients, str):
    -            recipients = [recipients]
    -        server = smtplib.SMTP(self.smtp_server, self.smtp_port)
    -        server.ehlo()
    -        if self._ssl:
    -            server.starttls()
    -        if self.login:
    -            server.login(self.login, self.password)
    -
    -        if mimetype is None:
    -            if "<html>" in message:
    -                mimetype = "html"
    -            else:
    -                mimetype = "plain"
    -
    -        msg = MIMEText(message, mimetype)
    -
    -        msg["Subject"] = subject
    -        msg["From"] = sender
    -        msg["To"] = ",".join(recipients)
    -
    -        if files:
    -            txtmsg = msg
    -            msg = MIMEMultipart()
    -            msg["Subject"] = subject
    -            msg["From"] = sender
    -            msg["To"] = ",".join(recipients)
    -            msg.attach(txtmsg)
    -            for fl in files:
    -                # Guess the content type based on the file's extension.  Encoding
    -                # will be ignored, although we should check for simple things like
    -                # gzip'd or compressed files.
    -                filename = j.sals.fs.basename(fl)
    -                ctype, encoding = mimetypes.guess_type(fl)
    -                content = j.sals.fs.read_file(fl)
    -                if ctype is None or encoding is not None:
    -                    # No guess could be made, or the file is encoded (compressed), so
    -                    # use a generic bag-of-bits type.
    -                    ctype = "application/octet-stream"
    -                maintype, subtype = ctype.split("/", 1)
    -                if maintype == "text":
    -                    attachement = MIMEText(content, _subtype=subtype)
    -                elif maintype == "image":
    -                    attachement = MIMEImage(content, _subtype=subtype)
    -                elif maintype == "audio":
    -                    attachement = MIMEAudio(content, _subtype=subtype)
    -                else:
    -                    attachement = MIMEBase(maintype, subtype)
    -                    attachement.set_payload(content)
    -                    # Encode the payload using Base64
    -                    encoders.encode_base64(attachement)
    -                # Set the filename parameter
    -                attachement.add_header("Content-Disposition", "attachment", filename=filename)
    -                msg.attach(attachement)
    -        server.sendmail(sender, recipients, msg.as_string())
    -        server.close()
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var is_ssl
    -
    -
    -
    -Source code -
    @property
    -def is_ssl(self):
    -    self.smtp_port in [465, 587]
    -
    -
    -
    var login
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var name
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var password
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var sender_email
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var smtp_port
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var smtp_server
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def send(self, recipients, sender='', subject='', message='', files=None, mimetype=None) -
    -
    -

    Send an email to the recipients from the sender containing the message required and any attached files given by the paths in files -:param recipients: Recipients of the message -:type recipients: mixed, str or list -:param sender: Sender of the email -:type sender: str -:param subject: Subject of the email -:type subject: str -:param message: Body of the email -:type message: str -:param files: List of paths to files to attach -:type files: list of strings -:param mimetype: Type of the body plain, html or None for autodetection -:type mimetype: str

    -
    -Source code -
    def send(self, recipients, sender="", subject="", message="", files=None, mimetype=None):
    -    """ Send an email to the recipients from the sender containing the message required and any attached files given by the paths in files
    -    :param recipients: Recipients of the message
    -    :type recipients: mixed, str or list
    -    :param sender: Sender of the email
    -    :type sender: str
    -    :param subject: Subject of the email
    -    :type subject: str
    -    :param message: Body of the email
    -    :type message: str
    -    :param files: List of paths to files to attach
    -    :type files: list of strings
    -    :param mimetype: Type of the body plain, html or None for autodetection
    -    :type mimetype: str
    -    """
    -    if not sender:
    -        sender = self.sender_email
    -    if isinstance(recipients, str):
    -        recipients = [recipients]
    -    server = smtplib.SMTP(self.smtp_server, self.smtp_port)
    -    server.ehlo()
    -    if self._ssl:
    -        server.starttls()
    -    if self.login:
    -        server.login(self.login, self.password)
    -
    -    if mimetype is None:
    -        if "<html>" in message:
    -            mimetype = "html"
    -        else:
    -            mimetype = "plain"
    -
    -    msg = MIMEText(message, mimetype)
    -
    -    msg["Subject"] = subject
    -    msg["From"] = sender
    -    msg["To"] = ",".join(recipients)
    -
    -    if files:
    -        txtmsg = msg
    -        msg = MIMEMultipart()
    -        msg["Subject"] = subject
    -        msg["From"] = sender
    -        msg["To"] = ",".join(recipients)
    -        msg.attach(txtmsg)
    -        for fl in files:
    -            # Guess the content type based on the file's extension.  Encoding
    -            # will be ignored, although we should check for simple things like
    -            # gzip'd or compressed files.
    -            filename = j.sals.fs.basename(fl)
    -            ctype, encoding = mimetypes.guess_type(fl)
    -            content = j.sals.fs.read_file(fl)
    -            if ctype is None or encoding is not None:
    -                # No guess could be made, or the file is encoded (compressed), so
    -                # use a generic bag-of-bits type.
    -                ctype = "application/octet-stream"
    -            maintype, subtype = ctype.split("/", 1)
    -            if maintype == "text":
    -                attachement = MIMEText(content, _subtype=subtype)
    -            elif maintype == "image":
    -                attachement = MIMEImage(content, _subtype=subtype)
    -            elif maintype == "audio":
    -                attachement = MIMEAudio(content, _subtype=subtype)
    -            else:
    -                attachement = MIMEBase(maintype, subtype)
    -                attachement.set_payload(content)
    -                # Encode the payload using Base64
    -                encoders.encode_base64(attachement)
    -            # Set the filename parameter
    -            attachement.add_header("Content-Disposition", "attachment", filename=filename)
    -            msg.attach(attachement)
    -    server.sendmail(sender, recipients, msg.as_string())
    -    server.close()
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/s3/index.html b/docs/api/jumpscale/clients/s3/index.html deleted file mode 100644 index 8576b6079..000000000 --- a/docs/api/jumpscale/clients/s3/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -jumpscale.clients.s3 API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.s3

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .s3 import S3Client
    -
    -    return StoredFactory(S3Client)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.s3.s3
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .s3 import S3Client
    -
    -    return StoredFactory(S3Client)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/s3/s3.html b/docs/api/jumpscale/clients/s3/s3.html deleted file mode 100644 index e86413912..000000000 --- a/docs/api/jumpscale/clients/s3/s3.html +++ /dev/null @@ -1,719 +0,0 @@ - - - - - - -jumpscale.clients.s3.s3 API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.s3.s3

    -
    -
    -
    -Source code -
    from jumpscale.clients.base import Client
    -from jumpscale.core.base import Base, fields
    -import urllib3
    -import certifi
    -
    -from minio import Minio
    -from minio.error import ResponseError, BucketAlreadyOwnedByYou, BucketAlreadyExists
    -
    -
    -class S3Client(Client):
    -    name = fields.String()
    -    address = fields.String()
    -    port = fields.Integer()
    -    access_key = fields.String()
    -    secret_key = fields.String()
    -    bucket = fields.String()
    -    create_bucket = fields.Boolean()
    -
    -    def __init__(self):
    -        super().__init__()
    -        # Create the http client to be able to set timeout
    -        http_client = urllib3.PoolManager(
    -            timeout=5,
    -            cert_reqs="CERT_REQUIRED",
    -            ca_certs=certifi.where(),
    -            retries=urllib3.Retry(total=3, backoff_factor=0.2, status_forcelist=[500, 502, 503, 504]),
    -        )
    -        # Create Minio client
    -        self.client = Minio(
    -            "{}:{}".format(self.address, self.port),
    -            access_key=self.access_key,
    -            secret_key=self.secret_key,
    -            secure=False,
    -            http_client=http_client,
    -        )
    -
    -        if self.bucket_create:
    -            self._bucket_create(self.bucket)
    -
    -    def _bucket_create(self, name):
    -        try:
    -            self.client.make_bucket(name, location="us-east-1")
    -        except BucketAlreadyOwnedByYou as err:
    -            pass
    -        except BucketAlreadyExists as err:
    -            pass
    -        except ResponseError as err:
    -            raise
    -
    -    def upload(self, bucket_name, object_name, file_path, content_type="text/plain", meta_data=None):
    -        """Upload contents from a file specified by file_path, to object_name
    -
    -        :param bucket_name: name of bucket
    -        :type bucket_name: str
    -        :param object_name: name of object
    -        :type object_name: str
    -        :param file_path: local path from which object data will be read
    -        :type file_path: str
    -        :param content_type: content type of the object, defaults to 'text/plain'
    -        :type content_type: str, optional
    -        :param meta_data: additional metadata, defaults to None
    -        :type meta_data: dict, optional
    -        :raises ValueError: if file given by file_path is not found
    -        :return: str
    -        :rtype: Object etag computed by the minio server.
    -        """
    -        if not j.sals.fs.exists(file_path):
    -            raise j.exceptions.Value("file: {} not found".format(file_path))
    -        return self.client.fput_object(bucket_name, object_name, file_path, content_type, meta_data)
    -
    -    def download(self, bucket_name, object_name, file_path):
    -        """Download and save the object as a file in the local filesystem
    -
    -        :param bucket_name: name of bucket
    -        :type bucket_name: str
    -        :param object_name: name of object
    -        :type object_name: str
    -        :param file_path: local path to which object data will be written
    -        :type file_path: str
    -        :return: object stat info (includes: size, etag, content_type,last_modified, metadata)
    -        :rtype: Object
    -        """
    -
    -        return self.client.fget_object(bucket_name, object_name, file_path)
    -
    -    def list_buckets(self):
    -        """List all buckets
    -
    -        :return: bucketList, bucket.name, bucket.creation_date
    -        :rtype: function, str, date
    -        """
    -        return self.client.list_buckets()
    -
    -    def list_objects(self, bucket_name, prefix=None, recursive=None):
    -        """List objects in a specific bucket
    -
    -        :param bucket_name: name of bucket
    -        :type bucket_name: str
    -        :param prefix: prefix of the objects that should be listed, defaults to None
    -        :type prefix: str, optional
    -        :param recursive: True indicates recursive style listing and False indicates directory style listing delimited by '/', defaults to None
    -        :type recursive: bool, optional
    -        :return: Iterator for all the objects in the bucket (includes: bucket_name, object_name,is_dir, size, etag, last_modified)
    -        :rtype: Object
    -        """
    -
    -        return self.client.list_objects(bucket_name, prefix=prefix, recursive=recursive)
    -
    -    def remove_bucket(self, bucket_name):
    -        """Remove a bucket.
    -
    -        :param bucket_name: name of bucket to be removed
    -        :type bucket_name: str
    -        """
    -        return self.client.remove_bucket(bucket_name)
    -
    -    def remove_object(self, bucket_name, object_name):
    -        """Remove object from bucket
    -
    -        :param bucket_name: name of bucket
    -        :type bucket_name: str
    -        :param object_name: name of object to be removed
    -        :type object_name: str
    -        """
    -
    -        return self.client.remove_object(bucket_name, object_name)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class S3Client -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class S3Client(Client):
    -    name = fields.String()
    -    address = fields.String()
    -    port = fields.Integer()
    -    access_key = fields.String()
    -    secret_key = fields.String()
    -    bucket = fields.String()
    -    create_bucket = fields.Boolean()
    -
    -    def __init__(self):
    -        super().__init__()
    -        # Create the http client to be able to set timeout
    -        http_client = urllib3.PoolManager(
    -            timeout=5,
    -            cert_reqs="CERT_REQUIRED",
    -            ca_certs=certifi.where(),
    -            retries=urllib3.Retry(total=3, backoff_factor=0.2, status_forcelist=[500, 502, 503, 504]),
    -        )
    -        # Create Minio client
    -        self.client = Minio(
    -            "{}:{}".format(self.address, self.port),
    -            access_key=self.access_key,
    -            secret_key=self.secret_key,
    -            secure=False,
    -            http_client=http_client,
    -        )
    -
    -        if self.bucket_create:
    -            self._bucket_create(self.bucket)
    -
    -    def _bucket_create(self, name):
    -        try:
    -            self.client.make_bucket(name, location="us-east-1")
    -        except BucketAlreadyOwnedByYou as err:
    -            pass
    -        except BucketAlreadyExists as err:
    -            pass
    -        except ResponseError as err:
    -            raise
    -
    -    def upload(self, bucket_name, object_name, file_path, content_type="text/plain", meta_data=None):
    -        """Upload contents from a file specified by file_path, to object_name
    -
    -        :param bucket_name: name of bucket
    -        :type bucket_name: str
    -        :param object_name: name of object
    -        :type object_name: str
    -        :param file_path: local path from which object data will be read
    -        :type file_path: str
    -        :param content_type: content type of the object, defaults to 'text/plain'
    -        :type content_type: str, optional
    -        :param meta_data: additional metadata, defaults to None
    -        :type meta_data: dict, optional
    -        :raises ValueError: if file given by file_path is not found
    -        :return: str
    -        :rtype: Object etag computed by the minio server.
    -        """
    -        if not j.sals.fs.exists(file_path):
    -            raise j.exceptions.Value("file: {} not found".format(file_path))
    -        return self.client.fput_object(bucket_name, object_name, file_path, content_type, meta_data)
    -
    -    def download(self, bucket_name, object_name, file_path):
    -        """Download and save the object as a file in the local filesystem
    -
    -        :param bucket_name: name of bucket
    -        :type bucket_name: str
    -        :param object_name: name of object
    -        :type object_name: str
    -        :param file_path: local path to which object data will be written
    -        :type file_path: str
    -        :return: object stat info (includes: size, etag, content_type,last_modified, metadata)
    -        :rtype: Object
    -        """
    -
    -        return self.client.fget_object(bucket_name, object_name, file_path)
    -
    -    def list_buckets(self):
    -        """List all buckets
    -
    -        :return: bucketList, bucket.name, bucket.creation_date
    -        :rtype: function, str, date
    -        """
    -        return self.client.list_buckets()
    -
    -    def list_objects(self, bucket_name, prefix=None, recursive=None):
    -        """List objects in a specific bucket
    -
    -        :param bucket_name: name of bucket
    -        :type bucket_name: str
    -        :param prefix: prefix of the objects that should be listed, defaults to None
    -        :type prefix: str, optional
    -        :param recursive: True indicates recursive style listing and False indicates directory style listing delimited by '/', defaults to None
    -        :type recursive: bool, optional
    -        :return: Iterator for all the objects in the bucket (includes: bucket_name, object_name,is_dir, size, etag, last_modified)
    -        :rtype: Object
    -        """
    -
    -        return self.client.list_objects(bucket_name, prefix=prefix, recursive=recursive)
    -
    -    def remove_bucket(self, bucket_name):
    -        """Remove a bucket.
    -
    -        :param bucket_name: name of bucket to be removed
    -        :type bucket_name: str
    -        """
    -        return self.client.remove_bucket(bucket_name)
    -
    -    def remove_object(self, bucket_name, object_name):
    -        """Remove object from bucket
    -
    -        :param bucket_name: name of bucket
    -        :type bucket_name: str
    -        :param object_name: name of object to be removed
    -        :type object_name: str
    -        """
    -
    -        return self.client.remove_object(bucket_name, object_name)
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var access_key
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var address
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var bucket
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var create_bucket
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var name
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var port
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var secret_key
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def download(self, bucket_name, object_name, file_path) -
    -
    -

    Download and save the object as a file in the local filesystem

    -

    :param bucket_name: name of bucket -:type bucket_name: str -:param object_name: name of object -:type object_name: str -:param file_path: local path to which object data will be written -:type file_path: str -:return: object stat info (includes: size, etag, content_type,last_modified, metadata) -:rtype: Object

    -
    -Source code -
    def download(self, bucket_name, object_name, file_path):
    -    """Download and save the object as a file in the local filesystem
    -
    -    :param bucket_name: name of bucket
    -    :type bucket_name: str
    -    :param object_name: name of object
    -    :type object_name: str
    -    :param file_path: local path to which object data will be written
    -    :type file_path: str
    -    :return: object stat info (includes: size, etag, content_type,last_modified, metadata)
    -    :rtype: Object
    -    """
    -
    -    return self.client.fget_object(bucket_name, object_name, file_path)
    -
    -
    -
    -def list_buckets(self) -
    -
    -

    List all buckets

    -

    :return: bucketList, bucket.name, bucket.creation_date -:rtype: function, str, date

    -
    -Source code -
    def list_buckets(self):
    -    """List all buckets
    -
    -    :return: bucketList, bucket.name, bucket.creation_date
    -    :rtype: function, str, date
    -    """
    -    return self.client.list_buckets()
    -
    -
    -
    -def list_objects(self, bucket_name, prefix=None, recursive=None) -
    -
    -

    List objects in a specific bucket

    -

    :param bucket_name: name of bucket -:type bucket_name: str -:param prefix: prefix of the objects that should be listed, defaults to None -:type prefix: str, optional -:param recursive: True indicates recursive style listing and False indicates directory style listing delimited by '/', defaults to None -:type recursive: bool, optional -:return: Iterator for all the objects in the bucket (includes: bucket_name, object_name,is_dir, size, etag, last_modified) -:rtype: Object

    -
    -Source code -
    def list_objects(self, bucket_name, prefix=None, recursive=None):
    -    """List objects in a specific bucket
    -
    -    :param bucket_name: name of bucket
    -    :type bucket_name: str
    -    :param prefix: prefix of the objects that should be listed, defaults to None
    -    :type prefix: str, optional
    -    :param recursive: True indicates recursive style listing and False indicates directory style listing delimited by '/', defaults to None
    -    :type recursive: bool, optional
    -    :return: Iterator for all the objects in the bucket (includes: bucket_name, object_name,is_dir, size, etag, last_modified)
    -    :rtype: Object
    -    """
    -
    -    return self.client.list_objects(bucket_name, prefix=prefix, recursive=recursive)
    -
    -
    -
    -def remove_bucket(self, bucket_name) -
    -
    -

    Remove a bucket.

    -

    :param bucket_name: name of bucket to be removed -:type bucket_name: str

    -
    -Source code -
    def remove_bucket(self, bucket_name):
    -    """Remove a bucket.
    -
    -    :param bucket_name: name of bucket to be removed
    -    :type bucket_name: str
    -    """
    -    return self.client.remove_bucket(bucket_name)
    -
    -
    -
    -def remove_object(self, bucket_name, object_name) -
    -
    -

    Remove object from bucket

    -

    :param bucket_name: name of bucket -:type bucket_name: str -:param object_name: name of object to be removed -:type object_name: str

    -
    -Source code -
    def remove_object(self, bucket_name, object_name):
    -    """Remove object from bucket
    -
    -    :param bucket_name: name of bucket
    -    :type bucket_name: str
    -    :param object_name: name of object to be removed
    -    :type object_name: str
    -    """
    -
    -    return self.client.remove_object(bucket_name, object_name)
    -
    -
    -
    -def upload(self, bucket_name, object_name, file_path, content_type='text/plain', meta_data=None) -
    -
    -

    Upload contents from a file specified by file_path, to object_name

    -

    :param bucket_name: name of bucket -:type bucket_name: str -:param object_name: name of object -:type object_name: str -:param file_path: local path from which object data will be read -:type file_path: str -:param content_type: content type of the object, defaults to 'text/plain' -:type content_type: str, optional -:param meta_data: additional metadata, defaults to None -:type meta_data: dict, optional -:raises ValueError: if file given by file_path is not found -:return: str -:rtype: Object etag computed by the minio server.

    -
    -Source code -
    def upload(self, bucket_name, object_name, file_path, content_type="text/plain", meta_data=None):
    -    """Upload contents from a file specified by file_path, to object_name
    -
    -    :param bucket_name: name of bucket
    -    :type bucket_name: str
    -    :param object_name: name of object
    -    :type object_name: str
    -    :param file_path: local path from which object data will be read
    -    :type file_path: str
    -    :param content_type: content type of the object, defaults to 'text/plain'
    -    :type content_type: str, optional
    -    :param meta_data: additional metadata, defaults to None
    -    :type meta_data: dict, optional
    -    :raises ValueError: if file given by file_path is not found
    -    :return: str
    -    :rtype: Object etag computed by the minio server.
    -    """
    -    if not j.sals.fs.exists(file_path):
    -        raise j.exceptions.Value("file: {} not found".format(file_path))
    -    return self.client.fput_object(bucket_name, object_name, file_path, content_type, meta_data)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/sendgrid/index.html b/docs/api/jumpscale/clients/sendgrid/index.html deleted file mode 100644 index dff1c7ffa..000000000 --- a/docs/api/jumpscale/clients/sendgrid/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -jumpscale.clients.sendgrid API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.sendgrid

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .sendgrid import SendGridClient
    -
    -    return StoredFactory(SendGridClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.sendgrid.sendgrid
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .sendgrid import SendGridClient
    -
    -    return StoredFactory(SendGridClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/sendgrid/sendgrid.html b/docs/api/jumpscale/clients/sendgrid/sendgrid.html deleted file mode 100644 index b4d36f57d..000000000 --- a/docs/api/jumpscale/clients/sendgrid/sendgrid.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - -jumpscale.clients.sendgrid.sendgrid API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.sendgrid.sendgrid

    -
    -
    -
    -Source code -
    import base64
    -import os
    -import io
    -import sendgrid
    -from sendgrid.helpers.mail import Mail, Attachment
    -from python_http_client.exceptions import HTTPError
    -from jumpscale.loader import j
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -
    -
    -class SendGridClient(Client):
    -    apikey = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -
    -    def build_attachment(self, filepath, typ="application/pdf"):
    -        """
    -        Returns a valid sendgrid attachment from typical attachment object.
    -        """
    -        data = io.BytesIO()
    -        with open(filepath, "rb") as f:
    -            while True:
    -                d = f.read(2 ** 20)
    -                if not d:
    -                    break
    -                data.write(d)
    -        data.seek(0)
    -        attachment = sendgrid.Attachment()
    -        attachment.file_content = j.data.serializers.base64.encode(data.read()).decode()
    -        attachment.file_type = typ
    -        attachment.file_name = os.path.basename(filepath)
    -        attachment.disposition = "attachment"
    -        return attachment
    -
    -    def send(self, sender, subject, html_content="<strong>Email</strong>", recipients=None, attachments=None):
    -        recipients = recipients or []
    -        attachments = attachments or []
    -        recipients = list(set(recipients))
    -        mail = Mail(from_email=sender, to_emails=recipients, subject=subject, html_content=html_content)
    -        for at in attachments:
    -            mail.add_attachment(at)
    -        try:
    -            sg = sendgrid.SendGridAPIClient(self.apikey)
    -            # response=sg.send(mail)
    -            response = sg.client.mail.send.post(request_body=mail.get())
    -            print(response.status_code)
    -            print(response.body)
    -            print(response.headers)
    -        except HTTPError as e:
    -            raise e
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class SendGridClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class SendGridClient(Client):
    -    apikey = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -
    -    def build_attachment(self, filepath, typ="application/pdf"):
    -        """
    -        Returns a valid sendgrid attachment from typical attachment object.
    -        """
    -        data = io.BytesIO()
    -        with open(filepath, "rb") as f:
    -            while True:
    -                d = f.read(2 ** 20)
    -                if not d:
    -                    break
    -                data.write(d)
    -        data.seek(0)
    -        attachment = sendgrid.Attachment()
    -        attachment.file_content = j.data.serializers.base64.encode(data.read()).decode()
    -        attachment.file_type = typ
    -        attachment.file_name = os.path.basename(filepath)
    -        attachment.disposition = "attachment"
    -        return attachment
    -
    -    def send(self, sender, subject, html_content="<strong>Email</strong>", recipients=None, attachments=None):
    -        recipients = recipients or []
    -        attachments = attachments or []
    -        recipients = list(set(recipients))
    -        mail = Mail(from_email=sender, to_emails=recipients, subject=subject, html_content=html_content)
    -        for at in attachments:
    -            mail.add_attachment(at)
    -        try:
    -            sg = sendgrid.SendGridAPIClient(self.apikey)
    -            # response=sg.send(mail)
    -            response = sg.client.mail.send.post(request_body=mail.get())
    -            print(response.status_code)
    -            print(response.body)
    -            print(response.headers)
    -        except HTTPError as e:
    -            raise e
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var apikey
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def build_attachment(self, filepath, typ='application/pdf') -
    -
    -

    Returns a valid sendgrid attachment from typical attachment object.

    -
    -Source code -
    def build_attachment(self, filepath, typ="application/pdf"):
    -    """
    -    Returns a valid sendgrid attachment from typical attachment object.
    -    """
    -    data = io.BytesIO()
    -    with open(filepath, "rb") as f:
    -        while True:
    -            d = f.read(2 ** 20)
    -            if not d:
    -                break
    -            data.write(d)
    -    data.seek(0)
    -    attachment = sendgrid.Attachment()
    -    attachment.file_content = j.data.serializers.base64.encode(data.read()).decode()
    -    attachment.file_type = typ
    -    attachment.file_name = os.path.basename(filepath)
    -    attachment.disposition = "attachment"
    -    return attachment
    -
    -
    -
    -def send(self, sender, subject, html_content='<strong>Email</strong>', recipients=None, attachments=None) -
    -
    -
    -
    -Source code -
    def send(self, sender, subject, html_content="<strong>Email</strong>", recipients=None, attachments=None):
    -    recipients = recipients or []
    -    attachments = attachments or []
    -    recipients = list(set(recipients))
    -    mail = Mail(from_email=sender, to_emails=recipients, subject=subject, html_content=html_content)
    -    for at in attachments:
    -        mail.add_attachment(at)
    -    try:
    -        sg = sendgrid.SendGridAPIClient(self.apikey)
    -        # response=sg.send(mail)
    -        response = sg.client.mail.send.post(request_body=mail.get())
    -        print(response.status_code)
    -        print(response.body)
    -        print(response.headers)
    -    except HTTPError as e:
    -        raise e
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/sonic/client.html b/docs/api/jumpscale/clients/sonic/client.html deleted file mode 100644 index 232d74b36..000000000 --- a/docs/api/jumpscale/clients/sonic/client.html +++ /dev/null @@ -1,293 +0,0 @@ - - - - - - -jumpscale.clients.sonic.client API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.sonic.client

    -
    -
    -
    -Source code -
    from sonic import SearchClient, IngestClient
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import Base, fields
    -
    -
    -class SonicClient(Client):
    -    host = fields.String(default="127.0.0.1")
    -    port = fields.Integer(default=1491)
    -    password = fields.String(default="pass")
    -
    -    def __init__(self):
    -        super().__init__()
    -
    -        self.cached_client_search = None
    -        self.cached_client_ingest = None
    -
    -        self.push = self.client_ingest.push
    -        self.pop = self.client_ingest.pop
    -        self.count = self.client_ingest.count
    -        self.flush = self.client_ingest.flush
    -        self.flush_collection = self.client_ingest.flush_collection
    -        self.flush_bucket = self.client_ingest.flush_bucket
    -        self.flush_object = self.client_ingest.flush_object
    -
    -        self.query = self.client_search.query
    -        self.suggest = self.client_search.suggest
    -
    -    @property
    -    def client_ingest(self):
    -        if not self.cached_client_ingest:
    -            self.cached_client_ingest = IngestClient(host=self.host, port=self.port, password=self.password)
    -        return self.cached_client_ingest
    -
    -    @property
    -    def client_search(self):
    -        if not self.cached_client_search:
    -            self.cached_client_search = SearchClient(host=self.host, port=self.port, password=self.password)
    -        return self.cached_client_search
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class SonicClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class SonicClient(Client):
    -    host = fields.String(default="127.0.0.1")
    -    port = fields.Integer(default=1491)
    -    password = fields.String(default="pass")
    -
    -    def __init__(self):
    -        super().__init__()
    -
    -        self.cached_client_search = None
    -        self.cached_client_ingest = None
    -
    -        self.push = self.client_ingest.push
    -        self.pop = self.client_ingest.pop
    -        self.count = self.client_ingest.count
    -        self.flush = self.client_ingest.flush
    -        self.flush_collection = self.client_ingest.flush_collection
    -        self.flush_bucket = self.client_ingest.flush_bucket
    -        self.flush_object = self.client_ingest.flush_object
    -
    -        self.query = self.client_search.query
    -        self.suggest = self.client_search.suggest
    -
    -    @property
    -    def client_ingest(self):
    -        if not self.cached_client_ingest:
    -            self.cached_client_ingest = IngestClient(host=self.host, port=self.port, password=self.password)
    -        return self.cached_client_ingest
    -
    -    @property
    -    def client_search(self):
    -        if not self.cached_client_search:
    -            self.cached_client_search = SearchClient(host=self.host, port=self.port, password=self.password)
    -        return self.cached_client_search
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var client_ingest
    -
    -
    -
    -Source code -
    @property
    -def client_ingest(self):
    -    if not self.cached_client_ingest:
    -        self.cached_client_ingest = IngestClient(host=self.host, port=self.port, password=self.password)
    -    return self.cached_client_ingest
    -
    -
    - -
    -
    -
    -Source code -
    @property
    -def client_search(self):
    -    if not self.cached_client_search:
    -        self.cached_client_search = SearchClient(host=self.host, port=self.port, password=self.password)
    -    return self.cached_client_search
    -
    -
    -
    var host
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var password
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var port
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/sonic/index.html b/docs/api/jumpscale/clients/sonic/index.html deleted file mode 100644 index ee3ca2c5d..000000000 --- a/docs/api/jumpscale/clients/sonic/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -jumpscale.clients.sonic API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.sonic

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .client import SonicClient
    -
    -    return StoredFactory(SonicClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.sonic.client
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .client import SonicClient
    -
    -    return StoredFactory(SonicClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/syncthing/index.html b/docs/api/jumpscale/clients/syncthing/index.html deleted file mode 100644 index 5c669c1c0..000000000 --- a/docs/api/jumpscale/clients/syncthing/index.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -jumpscale.clients.syncthing API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.syncthing

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .syncthing import SyncthingClient
    -
    -    return StoredFactory(SyncthingClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.syncthing.syncthing
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .syncthing import SyncthingClient
    -
    -    return StoredFactory(SyncthingClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/syncthing/syncthing.html b/docs/api/jumpscale/clients/syncthing/syncthing.html deleted file mode 100644 index d014f62aa..000000000 --- a/docs/api/jumpscale/clients/syncthing/syncthing.html +++ /dev/null @@ -1,789 +0,0 @@ - - - - - - -jumpscale.clients.syncthing.syncthing API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.syncthing.syncthing

    -
    -
    -
    -Source code -
    from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -from jumpscale.loader import j
    -
    -
    -class SyncthingClient(Client):
    -    host = fields.String()
    -    port = fields.Integer()
    -    apikey = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__url = None
    -        self.__session = None
    -        self.__config = None
    -
    -    @property
    -    def url(self):
    -        if not self.__url:
    -            self.__url = f"http://{self.host}:{self.port}/rest"
    -        return self.__url
    -
    -    @property
    -    def config(self):
    -        if not self.__config:
    -            self.__config = self._call("system/config")
    -        return self.__config
    -
    -    @property
    -    def session(self):
    -        if not self.__session:
    -            self.__session = j.tools.http.Session()
    -            self.__session.headers = {
    -                "Content-Type": "application/json",
    -                "User-Agent": "Syncthing Python client",
    -                "X-API-Key": self.apikey,
    -            }
    -        return self.__session
    -
    -    def _call(self, endpoint, method="get", data=None, return_json=True):
    -        method_call = getattr(self.session, method)
    -        response = method_call(f"{self.url}/{endpoint}", json=data)
    -        response.raise_for_status()
    -
    -        if return_json:
    -            return response.json()
    -        else:
    -            return response.content
    -
    -    def restart(self):
    -        self._call("system/restart", "post")
    -        if not j.sals.nettools.wait_connection_test(self.host, self.port, timeout=3):
    -            j.exceptions.Timeout("Server didn't start in 3 seconds")
    -
    -    def get_status(self):
    -        return self._call("system/status")
    -
    -    def get_id(self):
    -        return self.get_status()["myID"]
    -
    -    def reload_config(self):
    -        self.__config = None
    -
    -    def set_config(self, config=None):
    -        self._call("system/config", "post", config or self.config, False)
    -
    -    def get_folders(self):
    -        return self.config["folders"]
    -
    -    def get_devices(self):
    -        return self.config["devices"]
    -
    -    def _get_folder(self, name):
    -        for idx, folder in enumerate(self.get_folders()):
    -            if folder["id"] == name:
    -                return idx
    -
    -    def _get_device(self, name):
    -        for idx, device in enumerate(self.get_devices()):
    -            if device["name"] == name:
    -                return idx
    -
    -    def check_folder(self, name):
    -        return self._get_folder(name) is not None
    -
    -    def check_device(self, name):
    -        return self._get_device(name) is not None
    -
    -    def delete_folder(self, name):
    -        folders = self.get_folders()
    -        idx = self._get_folder(name)
    -        if idx:
    -            folders.pop(idx)
    -            return self.set_config(self.config)
    -
    -    def delete_device(self, name):
    -        devices = self.get_devices()
    -        idx = self._get_device(name)
    -        if idx:
    -            devices.pop(idx)
    -            return self.set_config(self.config)
    -
    -    def add_folder(
    -        self,
    -        name,
    -        path,
    -        ignore_perms=False,
    -        read_only=False,
    -        rescan_intervals=10,
    -        devices=None,
    -        overwrite=False,
    -    ):
    -        folders = self.get_folders()
    -        idx = self._get_folder(name)
    -        if idx:
    -            if overwrite:
    -                folders.pop(idx)
    -            else:
    -                raise j.exceptions.Input(f"Folder with name: {name} already exists")
    -        devices = devices or []
    -        my_id = self.get_id()
    -        if my_id not in devices:
    -            devices.append(my_id)
    -        devices = [{"deviceID": device} for device in devices]
    -        folder = {
    -            "autoNormalize": False,
    -            "copiers": 0,
    -            "devices": devices,
    -            "hashers": 0,
    -            "id": name,
    -            "ignoreDelete": False,
    -            "ignorePerms": ignore_perms,
    -            "invalid": "",
    -            "minDiskFreePct": 5,
    -            "order": "random",
    -            "path": path,
    -            "pullers": 0,
    -            "readOnly": read_only,
    -            "rescanIntervalS": rescan_intervals,
    -            "versioning": {"params": {}, "type": ""},
    -        }
    -        folders.append(folder)
    -        return self.set_config(self.config)
    -
    -    def add_device(
    -        self, name, device_id, introducer=False, compression="always", overwrite=False
    -    ):
    -        devices = self.get_devices()
    -        idx = self._get_device(name)
    -        if idx:
    -            if overwrite:
    -                devices.pop(idx)
    -            else:
    -                raise j.exceptions.Input(f"Device with name: {name} already exists")
    -        device = {
    -            "addresses": ["dynamic"],
    -            "certName": "",
    -            "compression": compression,
    -            "deviceID": device_id,
    -            "introducer": introducer,
    -            "name": name,
    -        }
    -        devices.append(device)
    -        return self.set_config(self.config)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class SyncthingClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class SyncthingClient(Client):
    -    host = fields.String()
    -    port = fields.Integer()
    -    apikey = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__url = None
    -        self.__session = None
    -        self.__config = None
    -
    -    @property
    -    def url(self):
    -        if not self.__url:
    -            self.__url = f"http://{self.host}:{self.port}/rest"
    -        return self.__url
    -
    -    @property
    -    def config(self):
    -        if not self.__config:
    -            self.__config = self._call("system/config")
    -        return self.__config
    -
    -    @property
    -    def session(self):
    -        if not self.__session:
    -            self.__session = j.tools.http.Session()
    -            self.__session.headers = {
    -                "Content-Type": "application/json",
    -                "User-Agent": "Syncthing Python client",
    -                "X-API-Key": self.apikey,
    -            }
    -        return self.__session
    -
    -    def _call(self, endpoint, method="get", data=None, return_json=True):
    -        method_call = getattr(self.session, method)
    -        response = method_call(f"{self.url}/{endpoint}", json=data)
    -        response.raise_for_status()
    -
    -        if return_json:
    -            return response.json()
    -        else:
    -            return response.content
    -
    -    def restart(self):
    -        self._call("system/restart", "post")
    -        if not j.sals.nettools.wait_connection_test(self.host, self.port, timeout=3):
    -            j.exceptions.Timeout("Server didn't start in 3 seconds")
    -
    -    def get_status(self):
    -        return self._call("system/status")
    -
    -    def get_id(self):
    -        return self.get_status()["myID"]
    -
    -    def reload_config(self):
    -        self.__config = None
    -
    -    def set_config(self, config=None):
    -        self._call("system/config", "post", config or self.config, False)
    -
    -    def get_folders(self):
    -        return self.config["folders"]
    -
    -    def get_devices(self):
    -        return self.config["devices"]
    -
    -    def _get_folder(self, name):
    -        for idx, folder in enumerate(self.get_folders()):
    -            if folder["id"] == name:
    -                return idx
    -
    -    def _get_device(self, name):
    -        for idx, device in enumerate(self.get_devices()):
    -            if device["name"] == name:
    -                return idx
    -
    -    def check_folder(self, name):
    -        return self._get_folder(name) is not None
    -
    -    def check_device(self, name):
    -        return self._get_device(name) is not None
    -
    -    def delete_folder(self, name):
    -        folders = self.get_folders()
    -        idx = self._get_folder(name)
    -        if idx:
    -            folders.pop(idx)
    -            return self.set_config(self.config)
    -
    -    def delete_device(self, name):
    -        devices = self.get_devices()
    -        idx = self._get_device(name)
    -        if idx:
    -            devices.pop(idx)
    -            return self.set_config(self.config)
    -
    -    def add_folder(
    -        self,
    -        name,
    -        path,
    -        ignore_perms=False,
    -        read_only=False,
    -        rescan_intervals=10,
    -        devices=None,
    -        overwrite=False,
    -    ):
    -        folders = self.get_folders()
    -        idx = self._get_folder(name)
    -        if idx:
    -            if overwrite:
    -                folders.pop(idx)
    -            else:
    -                raise j.exceptions.Input(f"Folder with name: {name} already exists")
    -        devices = devices or []
    -        my_id = self.get_id()
    -        if my_id not in devices:
    -            devices.append(my_id)
    -        devices = [{"deviceID": device} for device in devices]
    -        folder = {
    -            "autoNormalize": False,
    -            "copiers": 0,
    -            "devices": devices,
    -            "hashers": 0,
    -            "id": name,
    -            "ignoreDelete": False,
    -            "ignorePerms": ignore_perms,
    -            "invalid": "",
    -            "minDiskFreePct": 5,
    -            "order": "random",
    -            "path": path,
    -            "pullers": 0,
    -            "readOnly": read_only,
    -            "rescanIntervalS": rescan_intervals,
    -            "versioning": {"params": {}, "type": ""},
    -        }
    -        folders.append(folder)
    -        return self.set_config(self.config)
    -
    -    def add_device(
    -        self, name, device_id, introducer=False, compression="always", overwrite=False
    -    ):
    -        devices = self.get_devices()
    -        idx = self._get_device(name)
    -        if idx:
    -            if overwrite:
    -                devices.pop(idx)
    -            else:
    -                raise j.exceptions.Input(f"Device with name: {name} already exists")
    -        device = {
    -            "addresses": ["dynamic"],
    -            "certName": "",
    -            "compression": compression,
    -            "deviceID": device_id,
    -            "introducer": introducer,
    -            "name": name,
    -        }
    -        devices.append(device)
    -        return self.set_config(self.config)
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var apikey
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var config
    -
    -
    -
    -Source code -
    @property
    -def config(self):
    -    if not self.__config:
    -        self.__config = self._call("system/config")
    -    return self.__config
    -
    -
    -
    var host
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var port
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var session
    -
    -
    -
    -Source code -
    @property
    -def session(self):
    -    if not self.__session:
    -        self.__session = j.tools.http.Session()
    -        self.__session.headers = {
    -            "Content-Type": "application/json",
    -            "User-Agent": "Syncthing Python client",
    -            "X-API-Key": self.apikey,
    -        }
    -    return self.__session
    -
    -
    -
    var url
    -
    -
    -
    -Source code -
    @property
    -def url(self):
    -    if not self.__url:
    -        self.__url = f"http://{self.host}:{self.port}/rest"
    -    return self.__url
    -
    -
    -
    -

    Methods

    -
    -
    -def add_device(self, name, device_id, introducer=False, compression='always', overwrite=False) -
    -
    -
    -
    -Source code -
    def add_device(
    -    self, name, device_id, introducer=False, compression="always", overwrite=False
    -):
    -    devices = self.get_devices()
    -    idx = self._get_device(name)
    -    if idx:
    -        if overwrite:
    -            devices.pop(idx)
    -        else:
    -            raise j.exceptions.Input(f"Device with name: {name} already exists")
    -    device = {
    -        "addresses": ["dynamic"],
    -        "certName": "",
    -        "compression": compression,
    -        "deviceID": device_id,
    -        "introducer": introducer,
    -        "name": name,
    -    }
    -    devices.append(device)
    -    return self.set_config(self.config)
    -
    -
    -
    -def add_folder(self, name, path, ignore_perms=False, read_only=False, rescan_intervals=10, devices=None, overwrite=False) -
    -
    -
    -
    -Source code -
    def add_folder(
    -    self,
    -    name,
    -    path,
    -    ignore_perms=False,
    -    read_only=False,
    -    rescan_intervals=10,
    -    devices=None,
    -    overwrite=False,
    -):
    -    folders = self.get_folders()
    -    idx = self._get_folder(name)
    -    if idx:
    -        if overwrite:
    -            folders.pop(idx)
    -        else:
    -            raise j.exceptions.Input(f"Folder with name: {name} already exists")
    -    devices = devices or []
    -    my_id = self.get_id()
    -    if my_id not in devices:
    -        devices.append(my_id)
    -    devices = [{"deviceID": device} for device in devices]
    -    folder = {
    -        "autoNormalize": False,
    -        "copiers": 0,
    -        "devices": devices,
    -        "hashers": 0,
    -        "id": name,
    -        "ignoreDelete": False,
    -        "ignorePerms": ignore_perms,
    -        "invalid": "",
    -        "minDiskFreePct": 5,
    -        "order": "random",
    -        "path": path,
    -        "pullers": 0,
    -        "readOnly": read_only,
    -        "rescanIntervalS": rescan_intervals,
    -        "versioning": {"params": {}, "type": ""},
    -    }
    -    folders.append(folder)
    -    return self.set_config(self.config)
    -
    -
    -
    -def check_device(self, name) -
    -
    -
    -
    -Source code -
    def check_device(self, name):
    -    return self._get_device(name) is not None
    -
    -
    -
    -def check_folder(self, name) -
    -
    -
    -
    -Source code -
    def check_folder(self, name):
    -    return self._get_folder(name) is not None
    -
    -
    -
    -def delete_device(self, name) -
    -
    -
    -
    -Source code -
    def delete_device(self, name):
    -    devices = self.get_devices()
    -    idx = self._get_device(name)
    -    if idx:
    -        devices.pop(idx)
    -        return self.set_config(self.config)
    -
    -
    -
    -def delete_folder(self, name) -
    -
    -
    -
    -Source code -
    def delete_folder(self, name):
    -    folders = self.get_folders()
    -    idx = self._get_folder(name)
    -    if idx:
    -        folders.pop(idx)
    -        return self.set_config(self.config)
    -
    -
    -
    -def get_devices(self) -
    -
    -
    -
    -Source code -
    def get_devices(self):
    -    return self.config["devices"]
    -
    -
    -
    -def get_folders(self) -
    -
    -
    -
    -Source code -
    def get_folders(self):
    -    return self.config["folders"]
    -
    -
    -
    -def get_id(self) -
    -
    -
    -
    -Source code -
    def get_id(self):
    -    return self.get_status()["myID"]
    -
    -
    -
    -def get_status(self) -
    -
    -
    -
    -Source code -
    def get_status(self):
    -    return self._call("system/status")
    -
    -
    -
    -def reload_config(self) -
    -
    -
    -
    -Source code -
    def reload_config(self):
    -    self.__config = None
    -
    -
    -
    -def restart(self) -
    -
    -
    -
    -Source code -
    def restart(self):
    -    self._call("system/restart", "post")
    -    if not j.sals.nettools.wait_connection_test(self.host, self.port, timeout=3):
    -        j.exceptions.Timeout("Server didn't start in 3 seconds")
    -
    -
    -
    -def set_config(self, config=None) -
    -
    -
    -
    -Source code -
    def set_config(self, config=None):
    -    self._call("system/config", "post", config or self.config, False)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/trello/index.html b/docs/api/jumpscale/clients/trello/index.html deleted file mode 100644 index d301609e9..000000000 --- a/docs/api/jumpscale/clients/trello/index.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -jumpscale.clients.trello API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.trello

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .trello import TrelloClient
    -
    -    return StoredFactory(TrelloClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.trello.trello
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .trello import TrelloClient
    -
    -    return StoredFactory(TrelloClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/trello/trello.html b/docs/api/jumpscale/clients/trello/trello.html deleted file mode 100644 index f5021f9e2..000000000 --- a/docs/api/jumpscale/clients/trello/trello.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - -jumpscale.clients.trello.trello API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.trello.trello

    -
    -
    -
    -Source code -
    from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -from jumpscale.loader import j
    -from trello import TrelloClient as TrelloAPIClient
    -from trello.util import create_oauth_token
    -
    -
    -class TrelloClient(Client):
    -
    -    name = fields.String()
    -    api_key_ = fields.String()
    -    secret = fields.String()
    -    access_token = fields.String()
    -    acess_token_secret= fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__client = None
    -
    -    @property
    -    def trello_client(self):
    -
    -        if not self.token_secret:
    -            # print("**WILL TRY TO DO OAUTH SESSION")
    -            access_token = create_oauth_token(key=self.api_key, secret=self.secret)
    -            self.access_token_ = access_token["oauth_token"]
    -            self.access_token_secret = access_token["oauth_token_secret"]
    -
    -        self.client = TrelloAPIClient(api_key=self.api_key_, api_secret=self.secret, token=self.token, token_secret=self.token_secret)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class TrelloClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class TrelloClient(Client):
    -
    -    name = fields.String()
    -    api_key_ = fields.String()
    -    secret = fields.String()
    -    access_token = fields.String()
    -    acess_token_secret= fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__client = None
    -
    -    @property
    -    def trello_client(self):
    -
    -        if not self.token_secret:
    -            # print("**WILL TRY TO DO OAUTH SESSION")
    -            access_token = create_oauth_token(key=self.api_key, secret=self.secret)
    -            self.access_token_ = access_token["oauth_token"]
    -            self.access_token_secret = access_token["oauth_token_secret"]
    -
    -        self.client = TrelloAPIClient(api_key=self.api_key_, api_secret=self.secret, token=self.token, token_secret=self.token_secret)
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var access_token
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var acess_token_secret
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var api_key_
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var name
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var secret
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var trello_client
    -
    -
    -
    -Source code -
    @property
    -def trello_client(self):
    -
    -    if not self.token_secret:
    -        # print("**WILL TRY TO DO OAUTH SESSION")
    -        access_token = create_oauth_token(key=self.api_key, secret=self.secret)
    -        self.access_token_ = access_token["oauth_token"]
    -        self.access_token_secret = access_token["oauth_token_secret"]
    -
    -    self.client = TrelloAPIClient(api_key=self.api_key_, api_secret=self.secret, token=self.token, token_secret=self.token_secret)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/twilio/index.html b/docs/api/jumpscale/clients/twilio/index.html deleted file mode 100644 index 0bddcecc2..000000000 --- a/docs/api/jumpscale/clients/twilio/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -jumpscale.clients.twilio API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.twilio

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .twilio import TwilioSMSClient
    -
    -    StoredFactory(TwilioSMSClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.twilio.twilio
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -    from .twilio import TwilioSMSClient
    -
    -    StoredFactory(TwilioSMSClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/twilio/twilio.html b/docs/api/jumpscale/clients/twilio/twilio.html deleted file mode 100644 index b1f4baba5..000000000 --- a/docs/api/jumpscale/clients/twilio/twilio.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - -jumpscale.clients.twilio.twilio API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.twilio.twilio

    -
    -
    -
    -Source code -
    from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -
    -# Download the helper library from https://www.twilio.com/docs/python/install
    -from twilio.rest import Client as TwilioRestClient
    -
    -
    -class TwilioSMSClient(Client):
    -    """Your Account Sid and Auth Token from twilio.com/console
    -        DANGER! This is insecure. See http://twil.io/secure
    -    """
    -
    -    account_sid = fields.String()
    -    auth_token = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__client = None
    -
    -    @property
    -    def twilio_rest_client(self):
    -        if not self.__client:
    -            self.__client = TwilioRestClient(self.account_sid, self.auth_token)
    -        return self.__client
    -
    -    def send_sms(self, body, sender, recievers=None):
    -        """send sms
    -            Your Account Sid and Auth Token from twilio.com/console
    -            DANGER! This is insecure. See http://twil.io/secure
    -        Arguments:
    -            body (string) : the sms text
    -            sender (text) : sender full number ex: +15558675310
    -
    -        Keyword Arguments:
    -            recievers (list) : recievers phone numbers
    -        """
    -        recievers = recievers or []
    -        for r in recievers:
    -            message = self.twilio_rest_client.messages.create(body=body, from_=sender, to=r)
    -            print(message.sid)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class TwilioSMSClient -
    -
    -

    Your Account Sid and Auth Token from twilio.com/console -DANGER! This is insecure. See http://twil.io/secure

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class TwilioSMSClient(Client):
    -    """Your Account Sid and Auth Token from twilio.com/console
    -        DANGER! This is insecure. See http://twil.io/secure
    -    """
    -
    -    account_sid = fields.String()
    -    auth_token = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self.__client = None
    -
    -    @property
    -    def twilio_rest_client(self):
    -        if not self.__client:
    -            self.__client = TwilioRestClient(self.account_sid, self.auth_token)
    -        return self.__client
    -
    -    def send_sms(self, body, sender, recievers=None):
    -        """send sms
    -            Your Account Sid and Auth Token from twilio.com/console
    -            DANGER! This is insecure. See http://twil.io/secure
    -        Arguments:
    -            body (string) : the sms text
    -            sender (text) : sender full number ex: +15558675310
    -
    -        Keyword Arguments:
    -            recievers (list) : recievers phone numbers
    -        """
    -        recievers = recievers or []
    -        for r in recievers:
    -            message = self.twilio_rest_client.messages.create(body=body, from_=sender, to=r)
    -            print(message.sid)
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var account_sid
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var auth_token
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var twilio_rest_client
    -
    -
    -
    -Source code -
    @property
    -def twilio_rest_client(self):
    -    if not self.__client:
    -        self.__client = TwilioRestClient(self.account_sid, self.auth_token)
    -    return self.__client
    -
    -
    -
    -

    Methods

    -
    -
    -def send_sms(self, body, sender, recievers=None) -
    -
    -

    send sms -Your Account Sid and Auth Token from twilio.com/console -DANGER! This is insecure. See http://twil.io/secure

    -

    Arguments

    -

    body (string) : the sms text -sender (text) : sender full number ex: +15558675310 -Keyword Arguments: -recievers (list) : recievers phone numbers

    -
    -Source code -
    def send_sms(self, body, sender, recievers=None):
    -    """send sms
    -        Your Account Sid and Auth Token from twilio.com/console
    -        DANGER! This is insecure. See http://twil.io/secure
    -    Arguments:
    -        body (string) : the sms text
    -        sender (text) : sender full number ex: +15558675310
    -
    -    Keyword Arguments:
    -        recievers (list) : recievers phone numbers
    -    """
    -    recievers = recievers or []
    -    for r in recievers:
    -        message = self.twilio_rest_client.messages.create(body=body, from_=sender, to=r)
    -        print(message.sid)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/zerotier/index.html b/docs/api/jumpscale/clients/zerotier/index.html deleted file mode 100644 index e24357f31..000000000 --- a/docs/api/jumpscale/clients/zerotier/index.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -jumpscale.clients.zerotier API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.zerotier

    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .zerotier import ZerotierClient
    -
    -    return StoredFactory(ZerotierClient)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.clients.zerotier.zerotier
    -
    -

    A client that gives an interface to the zerotier api …

    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from jumpscale.core.base import StoredFactory
    -
    -    from .zerotier import ZerotierClient
    -
    -    return StoredFactory(ZerotierClient)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/clients/zerotier/zerotier.html b/docs/api/jumpscale/clients/zerotier/zerotier.html deleted file mode 100644 index fedf24a9b..000000000 --- a/docs/api/jumpscale/clients/zerotier/zerotier.html +++ /dev/null @@ -1,1048 +0,0 @@ - - - - - - -jumpscale.clients.zerotier.zerotier API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.clients.zerotier.zerotier

    -
    -
    -

    A client that gives an interface to the zerotier api

    -
    zcl = j.clients.zerotier.get("instance")
    -
    -network = zcl.get_network("network_id")
    -
    -#  list routes
    -network.routes
    -
    -# add members
    -
    -member = network.add_member(address, name="guest")
    -
    -# Authorize member to this network
    -member.authorize()
    -
    -
    -
    -Source code -
    """
    -A client that gives an interface to the zerotier api
    -
    -```python
    -zcl = j.clients.zerotier.get("instance")
    -
    -network = zcl.get_network("network_id")
    -
    -#  list routes
    -network.routes
    -
    -# add members
    -
    -member = network.add_member(address, name="guest")
    -
    -# Authorize member to this network
    -member.authorize()
    -
    -```
    -
    -"""
    -
    -
    -import requests
    -import ipaddress
    -from jumpscale.loader import j
    -from jumpscale.clients.base import Client
    -from jumpscale.core.base import fields
    -
    -
    -class Member:
    -    def __init__(self, member_data, network, address=None):
    -        self.raw_data = member_data
    -        self._address = address
    -        self._network = network
    -        self.id = member_data["id"]
    -
    -    @property
    -    def address(self):
    -        if not self._address:
    -            self._address = self.identity.split(":")[0]
    -        return self._address
    -
    -    @property
    -    def identity(self):
    -        return self.raw_data["config"]["identity"]
    -
    -    @property
    -    def private_ip(self):
    -        return self.raw_data["config"]["ipAssignments"]
    -
    -    def _update_authorization(self, authorize):
    -        if self.raw_data["config"]["authorized"] == authorize:
    -            return
    -        self._network.update_member(self.address, authorized=authorize)
    -        self.raw_data["config"]["authorized"] = authorize
    -
    -    def authorize(self):
    -        """Authorize member to the zerotier network
    -        """
    -        self._update_authorization(True)
    -
    -    def unauthorize(self):
    -        """Unauthorize member to the zerotier network
    -        """
    -        self._update_authorization(False)
    -
    -    def __repr__(self):
    -        return f"Member({self.id})"
    -
    -
    -class Network:
    -    def __init__(self, network_data, client):
    -        self.id = network_data["id"]
    -        self._client = client
    -        self.raw_data = network_data
    -
    -    @property
    -    def name(self):
    -        return self.raw_data["config"]["name"]
    -
    -    @property
    -    def routes(self):
    -        return self.raw_data["config"]["routes"]
    -
    -    def list_members(self):
    -        """List all members of  that network
    -
    -        Returns:
    -            list: List of all members
    -        """
    -        return [Member(member, self) for member in self._client._send_request(f"network/{self.id}/member")]
    -
    -    def add_member(self, address, name=None, private_ip=None, authorized=True):
    -        """Add a member to the network
    -
    -        Args:
    -            address (str): Address of the member
    -            name (str, optional): Name of the member. Defaults to None.
    -            private_ip (str, optional): Private IP to assign. Defaults to None.
    -            authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    -
    -        Returns:
    -            Member: Added member
    -        """
    -        data = {"config": {"authorized": authorized}, "name": name}
    -        if private_ip:
    -            data["config"]["ipAssignments"] = [private_ip]
    -        member_data = self._client._send_request(f"network/{self.id}/member/{address}", method="post", data=data)
    -        return Member(member_data, self, address)
    -
    -    def update_member(self, address, name=None, private_ip=None, authorized=None):
    -        """Update a member in the network
    -
    -        Args:
    -            address (str): Address of the member
    -            name (str, optional): Name of the member. Defaults to None.
    -            private_ip (str, optional): Private IP to assign. Defaults to None.
    -            authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    -
    -        Returns:
    -            Member: Added member
    -        """
    -        return self.add_member(address, name, private_ip, authorized)
    -
    -    def get_member(self, address):
    -        """Returns member by address
    -
    -        Args:
    -            address (str): Member address
    -
    -        Returns:
    -            Member: Found member
    -        """
    -        return Member(self._client._send_request(f"network/{self.id}/member/{address}"), self, address)
    -
    -    def delete_member(self, address):
    -        """Deletes a member
    -
    -        Args:
    -            address (str): Member address
    -        """
    -        self._client._send_request(f"network/{self.id}/member/{address}", method="delete", return_json=False)
    -
    -    def __repr__(self):
    -        return f"Network({self.id})"
    -
    -
    -class ZerotierClient(Client):
    -    base_url = fields.String(default="https://my.zerotier.com/api")
    -    token = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self._session = None
    -
    -    def _send_request(self, path, method="get", data=None, return_json=True):
    -        url = f"{self.base_url}/{path}"
    -        func_method = getattr(self.session, method)
    -        res = func_method(url, json=data)
    -        res.raise_for_status()
    -        if return_json:
    -            return res.json()
    -        else:
    -            return res.content
    -
    -    @property
    -    def session(self):
    -        if not self._session:
    -            if not self.token:
    -                raise j.exceptions.Value("Please set token to use the client")
    -            self._session = requests.Session()
    -            self._session.headers["Authorization"] = f"Bearer {self.token}"
    -        return self._session
    -
    -    def list_networks(self):
    -        """List networks available to user
    -
    -        Returns:
    -            list: All available networks
    -        """
    -        return [Network(network, self) for network in self._send_request("network")]
    -
    -    def get_network(self, network_id):
    -        """Get network by id
    -
    -        Args:
    -            network_id (str): Network id
    -
    -        Returns:
    -            Network: Found network
    -        """
    -        return Network(self._send_request(f"network/{network_id}"), self)
    -
    -    def create_network(self, public, target_subnet=None, name=None, auto_assign=True):
    -        """Create a new network
    -
    -        Args:
    -            public (bool): Specify if network is public or not
    -            target_subnet (str, optional): Target network to be pick assignment ips from. Defaults to None.
    -            name (str, optional): Name of the network. Defaults to None.
    -            auto_assign (bool, optional): If true auto assign addresses. Defaults to True.
    -
    -        Returns:
    -            Network: Created network
    -        """
    -        routes = None
    -        config = {"private": not public, "noAutoAssignIps": not auto_assign, "routes": routes}
    -        if target_subnet:
    -            routes.append({"target": target_subnet, "via": None})
    -            network = ipaddress.IPv4Network(target_subnet)
    -            config["ipAssignmentPools"] = [{"ipRangeStart": network[0], "ipRangeEnd": network[-1]}]
    -
    -        if name:
    -            config.update({"name": name})
    -
    -        data = {"config": config}
    -        return Network(self._send_request(f"network", data=data, method="post"), self)
    -
    -    def delete_network(self, network_id):
    -        """Deletes network
    -
    -        Args:
    -            network_id (str): Network id
    -        """
    -        self._send_request(f"network/{network_id}", method="delete", return_json=False)
    -
    -    def get_status(self):
    -        return self._send_request("status")
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Member -(member_data, network, address=None) -
    -
    -
    -
    -Source code -
    class Member:
    -    def __init__(self, member_data, network, address=None):
    -        self.raw_data = member_data
    -        self._address = address
    -        self._network = network
    -        self.id = member_data["id"]
    -
    -    @property
    -    def address(self):
    -        if not self._address:
    -            self._address = self.identity.split(":")[0]
    -        return self._address
    -
    -    @property
    -    def identity(self):
    -        return self.raw_data["config"]["identity"]
    -
    -    @property
    -    def private_ip(self):
    -        return self.raw_data["config"]["ipAssignments"]
    -
    -    def _update_authorization(self, authorize):
    -        if self.raw_data["config"]["authorized"] == authorize:
    -            return
    -        self._network.update_member(self.address, authorized=authorize)
    -        self.raw_data["config"]["authorized"] = authorize
    -
    -    def authorize(self):
    -        """Authorize member to the zerotier network
    -        """
    -        self._update_authorization(True)
    -
    -    def unauthorize(self):
    -        """Unauthorize member to the zerotier network
    -        """
    -        self._update_authorization(False)
    -
    -    def __repr__(self):
    -        return f"Member({self.id})"
    -
    -

    Instance variables

    -
    -
    var address
    -
    -
    -
    -Source code -
    @property
    -def address(self):
    -    if not self._address:
    -        self._address = self.identity.split(":")[0]
    -    return self._address
    -
    -
    -
    var identity
    -
    -
    -
    -Source code -
    @property
    -def identity(self):
    -    return self.raw_data["config"]["identity"]
    -
    -
    -
    var private_ip
    -
    -
    -
    -Source code -
    @property
    -def private_ip(self):
    -    return self.raw_data["config"]["ipAssignments"]
    -
    -
    -
    -

    Methods

    -
    -
    -def authorize(self) -
    -
    -

    Authorize member to the zerotier network

    -
    -Source code -
    def authorize(self):
    -    """Authorize member to the zerotier network
    -    """
    -    self._update_authorization(True)
    -
    -
    -
    -def unauthorize(self) -
    -
    -

    Unauthorize member to the zerotier network

    -
    -Source code -
    def unauthorize(self):
    -    """Unauthorize member to the zerotier network
    -    """
    -    self._update_authorization(False)
    -
    -
    -
    -
    -
    -class Network -(network_data, client) -
    -
    -
    -
    -Source code -
    class Network:
    -    def __init__(self, network_data, client):
    -        self.id = network_data["id"]
    -        self._client = client
    -        self.raw_data = network_data
    -
    -    @property
    -    def name(self):
    -        return self.raw_data["config"]["name"]
    -
    -    @property
    -    def routes(self):
    -        return self.raw_data["config"]["routes"]
    -
    -    def list_members(self):
    -        """List all members of  that network
    -
    -        Returns:
    -            list: List of all members
    -        """
    -        return [Member(member, self) for member in self._client._send_request(f"network/{self.id}/member")]
    -
    -    def add_member(self, address, name=None, private_ip=None, authorized=True):
    -        """Add a member to the network
    -
    -        Args:
    -            address (str): Address of the member
    -            name (str, optional): Name of the member. Defaults to None.
    -            private_ip (str, optional): Private IP to assign. Defaults to None.
    -            authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    -
    -        Returns:
    -            Member: Added member
    -        """
    -        data = {"config": {"authorized": authorized}, "name": name}
    -        if private_ip:
    -            data["config"]["ipAssignments"] = [private_ip]
    -        member_data = self._client._send_request(f"network/{self.id}/member/{address}", method="post", data=data)
    -        return Member(member_data, self, address)
    -
    -    def update_member(self, address, name=None, private_ip=None, authorized=None):
    -        """Update a member in the network
    -
    -        Args:
    -            address (str): Address of the member
    -            name (str, optional): Name of the member. Defaults to None.
    -            private_ip (str, optional): Private IP to assign. Defaults to None.
    -            authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    -
    -        Returns:
    -            Member: Added member
    -        """
    -        return self.add_member(address, name, private_ip, authorized)
    -
    -    def get_member(self, address):
    -        """Returns member by address
    -
    -        Args:
    -            address (str): Member address
    -
    -        Returns:
    -            Member: Found member
    -        """
    -        return Member(self._client._send_request(f"network/{self.id}/member/{address}"), self, address)
    -
    -    def delete_member(self, address):
    -        """Deletes a member
    -
    -        Args:
    -            address (str): Member address
    -        """
    -        self._client._send_request(f"network/{self.id}/member/{address}", method="delete", return_json=False)
    -
    -    def __repr__(self):
    -        return f"Network({self.id})"
    -
    -

    Instance variables

    -
    -
    var name
    -
    -
    -
    -Source code -
    @property
    -def name(self):
    -    return self.raw_data["config"]["name"]
    -
    -
    -
    var routes
    -
    -
    -
    -Source code -
    @property
    -def routes(self):
    -    return self.raw_data["config"]["routes"]
    -
    -
    -
    -

    Methods

    -
    -
    -def add_member(self, address, name=None, private_ip=None, authorized=True) -
    -
    -

    Add a member to the network

    -

    Args

    -
    -
    address : str
    -
    Address of the member
    -
    name : str, optional
    -
    Name of the member. Defaults to None.
    -
    private_ip : str, optional
    -
    Private IP to assign. Defaults to None.
    -
    authorized : bool, optional
    -
    Whether to authorize the user or not. Defaults to True.
    -
    -

    Returns

    -
    -
    Member
    -
    Added member
    -
    -
    -Source code -
    def add_member(self, address, name=None, private_ip=None, authorized=True):
    -    """Add a member to the network
    -
    -    Args:
    -        address (str): Address of the member
    -        name (str, optional): Name of the member. Defaults to None.
    -        private_ip (str, optional): Private IP to assign. Defaults to None.
    -        authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    -
    -    Returns:
    -        Member: Added member
    -    """
    -    data = {"config": {"authorized": authorized}, "name": name}
    -    if private_ip:
    -        data["config"]["ipAssignments"] = [private_ip]
    -    member_data = self._client._send_request(f"network/{self.id}/member/{address}", method="post", data=data)
    -    return Member(member_data, self, address)
    -
    -
    -
    -def delete_member(self, address) -
    -
    -

    Deletes a member

    -

    Args

    -
    -
    address : str
    -
    Member address
    -
    -
    -Source code -
    def delete_member(self, address):
    -    """Deletes a member
    -
    -    Args:
    -        address (str): Member address
    -    """
    -    self._client._send_request(f"network/{self.id}/member/{address}", method="delete", return_json=False)
    -
    -
    -
    -def get_member(self, address) -
    -
    -

    Returns member by address

    -

    Args

    -
    -
    address : str
    -
    Member address
    -
    -

    Returns

    -
    -
    Member
    -
    Found member
    -
    -
    -Source code -
    def get_member(self, address):
    -    """Returns member by address
    -
    -    Args:
    -        address (str): Member address
    -
    -    Returns:
    -        Member: Found member
    -    """
    -    return Member(self._client._send_request(f"network/{self.id}/member/{address}"), self, address)
    -
    -
    -
    -def list_members(self) -
    -
    -

    List all members of -that network

    -

    Returns

    -
    -
    list
    -
    List of all members
    -
    -
    -Source code -
    def list_members(self):
    -    """List all members of  that network
    -
    -    Returns:
    -        list: List of all members
    -    """
    -    return [Member(member, self) for member in self._client._send_request(f"network/{self.id}/member")]
    -
    -
    -
    -def update_member(self, address, name=None, private_ip=None, authorized=None) -
    -
    -

    Update a member in the network

    -

    Args

    -
    -
    address : str
    -
    Address of the member
    -
    name : str, optional
    -
    Name of the member. Defaults to None.
    -
    private_ip : str, optional
    -
    Private IP to assign. Defaults to None.
    -
    authorized : bool, optional
    -
    Whether to authorize the user or not. Defaults to True.
    -
    -

    Returns

    -
    -
    Member
    -
    Added member
    -
    -
    -Source code -
    def update_member(self, address, name=None, private_ip=None, authorized=None):
    -    """Update a member in the network
    -
    -    Args:
    -        address (str): Address of the member
    -        name (str, optional): Name of the member. Defaults to None.
    -        private_ip (str, optional): Private IP to assign. Defaults to None.
    -        authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    -
    -    Returns:
    -        Member: Added member
    -    """
    -    return self.add_member(address, name, private_ip, authorized)
    -
    -
    -
    -
    -
    -class ZerotierClient -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class ZerotierClient(Client):
    -    base_url = fields.String(default="https://my.zerotier.com/api")
    -    token = fields.String()
    -
    -    def __init__(self):
    -        super().__init__()
    -        self._session = None
    -
    -    def _send_request(self, path, method="get", data=None, return_json=True):
    -        url = f"{self.base_url}/{path}"
    -        func_method = getattr(self.session, method)
    -        res = func_method(url, json=data)
    -        res.raise_for_status()
    -        if return_json:
    -            return res.json()
    -        else:
    -            return res.content
    -
    -    @property
    -    def session(self):
    -        if not self._session:
    -            if not self.token:
    -                raise j.exceptions.Value("Please set token to use the client")
    -            self._session = requests.Session()
    -            self._session.headers["Authorization"] = f"Bearer {self.token}"
    -        return self._session
    -
    -    def list_networks(self):
    -        """List networks available to user
    -
    -        Returns:
    -            list: All available networks
    -        """
    -        return [Network(network, self) for network in self._send_request("network")]
    -
    -    def get_network(self, network_id):
    -        """Get network by id
    -
    -        Args:
    -            network_id (str): Network id
    -
    -        Returns:
    -            Network: Found network
    -        """
    -        return Network(self._send_request(f"network/{network_id}"), self)
    -
    -    def create_network(self, public, target_subnet=None, name=None, auto_assign=True):
    -        """Create a new network
    -
    -        Args:
    -            public (bool): Specify if network is public or not
    -            target_subnet (str, optional): Target network to be pick assignment ips from. Defaults to None.
    -            name (str, optional): Name of the network. Defaults to None.
    -            auto_assign (bool, optional): If true auto assign addresses. Defaults to True.
    -
    -        Returns:
    -            Network: Created network
    -        """
    -        routes = None
    -        config = {"private": not public, "noAutoAssignIps": not auto_assign, "routes": routes}
    -        if target_subnet:
    -            routes.append({"target": target_subnet, "via": None})
    -            network = ipaddress.IPv4Network(target_subnet)
    -            config["ipAssignmentPools"] = [{"ipRangeStart": network[0], "ipRangeEnd": network[-1]}]
    -
    -        if name:
    -            config.update({"name": name})
    -
    -        data = {"config": config}
    -        return Network(self._send_request(f"network", data=data, method="post"), self)
    -
    -    def delete_network(self, network_id):
    -        """Deletes network
    -
    -        Args:
    -            network_id (str): Network id
    -        """
    -        self._send_request(f"network/{network_id}", method="delete", return_json=False)
    -
    -    def get_status(self):
    -        return self._send_request("status")
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var base_url
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    var session
    -
    -
    -
    -Source code -
    @property
    -def session(self):
    -    if not self._session:
    -        if not self.token:
    -            raise j.exceptions.Value("Please set token to use the client")
    -        self._session = requests.Session()
    -        self._session.headers["Authorization"] = f"Bearer {self.token}"
    -    return self._session
    -
    -
    -
    var token
    -
    -

    getter method this property

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    Returns:
    -        any: the field value
    -    """
    -    # if it's already defined, just return it
    -    if hasattr(self, inner_name):
    -        return getattr(self, inner_name)
    -
    -    # if not, it will just return the default value of the field
    -    # accept raw value as default too
    -    return field.from_raw(field.default)
    -
    -
    -
    -

    Methods

    -
    -
    -def create_network(self, public, target_subnet=None, name=None, auto_assign=True) -
    -
    -

    Create a new network

    -

    Args

    -
    -
    public : bool
    -
    Specify if network is public or not
    -
    target_subnet : str, optional
    -
    Target network to be pick assignment ips from. Defaults to None.
    -
    name : str, optional
    -
    Name of the network. Defaults to None.
    -
    auto_assign : bool, optional
    -
    If true auto assign addresses. Defaults to True.
    -
    -

    Returns

    -
    -
    Network
    -
    Created network
    -
    -
    -Source code -
    def create_network(self, public, target_subnet=None, name=None, auto_assign=True):
    -    """Create a new network
    -
    -    Args:
    -        public (bool): Specify if network is public or not
    -        target_subnet (str, optional): Target network to be pick assignment ips from. Defaults to None.
    -        name (str, optional): Name of the network. Defaults to None.
    -        auto_assign (bool, optional): If true auto assign addresses. Defaults to True.
    -
    -    Returns:
    -        Network: Created network
    -    """
    -    routes = None
    -    config = {"private": not public, "noAutoAssignIps": not auto_assign, "routes": routes}
    -    if target_subnet:
    -        routes.append({"target": target_subnet, "via": None})
    -        network = ipaddress.IPv4Network(target_subnet)
    -        config["ipAssignmentPools"] = [{"ipRangeStart": network[0], "ipRangeEnd": network[-1]}]
    -
    -    if name:
    -        config.update({"name": name})
    -
    -    data = {"config": config}
    -    return Network(self._send_request(f"network", data=data, method="post"), self)
    -
    -
    -
    -def delete_network(self, network_id) -
    -
    -

    Deletes network

    -

    Args

    -
    -
    network_id : str
    -
    Network id
    -
    -
    -Source code -
    def delete_network(self, network_id):
    -    """Deletes network
    -
    -    Args:
    -        network_id (str): Network id
    -    """
    -    self._send_request(f"network/{network_id}", method="delete", return_json=False)
    -
    -
    -
    -def get_network(self, network_id) -
    -
    -

    Get network by id

    -

    Args

    -
    -
    network_id : str
    -
    Network id
    -
    -

    Returns

    -
    -
    Network
    -
    Found network
    -
    -
    -Source code -
    def get_network(self, network_id):
    -    """Get network by id
    -
    -    Args:
    -        network_id (str): Network id
    -
    -    Returns:
    -        Network: Found network
    -    """
    -    return Network(self._send_request(f"network/{network_id}"), self)
    -
    -
    -
    -def get_status(self) -
    -
    -
    -
    -Source code -
    def get_status(self):
    -    return self._send_request("status")
    -
    -
    -
    -def list_networks(self) -
    -
    -

    List networks available to user

    -

    Returns

    -
    -
    list
    -
    All available networks
    -
    -
    -Source code -
    def list_networks(self):
    -    """List networks available to user
    -
    -    Returns:
    -        list: All available networks
    -    """
    -    return [Network(network, self) for network in self._send_request("network")]
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/core/base/store.html b/docs/api/jumpscale/core/base/store.html deleted file mode 100644 index 500d661ec..000000000 --- a/docs/api/jumpscale/core/base/store.html +++ /dev/null @@ -1,1941 +0,0 @@ - - - - - - -jumpscale.core.base.store API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.core.base.store

    -
    -
    -

    Store defines the interface for the backend storage, let it be filesystem or redis.

    -

    This module also defines the abstractions needed for any storage backend.

    -

    Every backend should be able to organize configuration for multiple instance given a location, also -read/write the config data in raw (string) format.

    -
    -Source code -
    """
    -Store defines the interface for the backend storage, let it be filesystem or redis.
    -
    -This module also defines the abstractions needed for any storage backend.
    -
    -Every backend should be able to organize configuration for multiple instance given a location, also
    -read/write the config data in raw (string) format.
    -"""
    -
    -
    -import os
    -import shutil
    -
    -import redis
    -
    -from abc import ABC, abstractmethod
    -from enum import Enum
    -
    -from jumpscale.data.nacl import NACL
    -from jumpscale.data.serializers import base64, json
    -from jumpscale.core.config import Environment
    -from jumpscale.sals.fs import read_file_binary, write_file_binary, rmtree
    -
    -
    -class InvalidPrivateKey(Exception):
    -    """
    -    raised when the private key configured is invalid
    -    """
    -
    -
    -class Location:
    -    """
    -    dot-separated auto-location for any type
    -
    -    for example, if we have a class in jumpscale/clients/redis/<type>
    -    location name will be jumpscale.clients.redis.<type>
    -    """
    -
    -    def __init__(self, *name_list):
    -        self.name_list = list(name_list)
    -
    -    @property
    -    def name(self):
    -        """
    -        get dot seprated string from current name list
    -
    -        Returns:
    -            str: dot separated string
    -        """
    -        return ".".join(self.name_list)
    -
    -    @property
    -    def path(self):
    -        """
    -        get a filesystem path with from `name`, where dots are replaced by `os.sep`
    -
    -        Returns:
    -            str: path
    -        """
    -        return os.path.join(*self.name.split("."))
    -
    -    @classmethod
    -    def from_type(cls, type_):
    -        """
    -        get a location from any type/class
    -
    -        Args:
    -            type_ (type): any type (class)
    -
    -        Returns:
    -            Location: a location object
    -        """
    -        return cls(type_.__module__, type_.__name__)
    -
    -    def __str__(self):
    -        args = "', '".join(self.name_list)
    -        cls_name = self.__class__.__name__
    -        return f"{cls_name}('{args}')"
    -
    -    __repr__ = __str__
    -
    -
    -class EncryptionMode(Enum):
    -    """
    -    An enum to select encryption mode based on loading or storing the data
    -    """
    -
    -    Encrypt = 0
    -    Decrypt = 1
    -
    -
    -class EncryptionMixin:
    -    """
    -    A mixin that provides encrypt and decrypt methods, which can be used in any store
    -    """
    -
    -    def encrypt(self, data):
    -        """encrypt data
    -
    -        Args:
    -            data (str): input string
    -
    -        Returns:
    -            bytes: encrypted data as byte string
    -        """
    -        if not isinstance(data, bytes):
    -            data = data.encode()
    -        return self.nacl.encrypt(data, self.public_key)
    -
    -    def decrypt(self, data):
    -        """decrypt data
    -
    -        Args:
    -            data (bytes): encrypted byte string
    -
    -        Returns:
    -            str: decrypted data
    -        """
    -        return self.nacl.decrypt(data, self.public_key).decode()
    -
    -
    -class ConfigStore(ABC):
    -    """
    -    the interface every config store should implement:
    -
    -    - `read(instance_name)`:  reads the data of this instance name
    -    - `write(instance_name, data)`: writes the data of this instance
    -    - `list_all(instance_name)`: lists all instance names
    -    - `delete(instance_name)`: delete instance data
    -    """
    -
    -    @abstractmethod
    -    def read(self, instance_name):
    -        pass
    -
    -    @abstractmethod
    -    def write(self, instance_name, data):
    -        pass
    -
    -    @abstractmethod
    -    def list_all(self):
    -        pass
    -
    -    @abstractmethod
    -    def delete(self, instance_name):
    -        pass
    -
    -
    -class EncryptedConfigStore(ConfigStore, EncryptionMixin):
    -    """the base class for any config store backend"""
    -
    -    def __init__(self, location):
    -        self.location = location
    -        self.config_env = Environment()
    -        self.priv_key = base64.decode(self.config_env.get_private_key())
    -        self.nacl = NACL(private_key=self.priv_key)
    -        self.public_key = self.nacl.public_key.encode()
    -
    -        if not self.priv_key:
    -            raise InvalidPrivateKey
    -
    -    def _encrypt_value(self, value):
    -        """
    -        encrypt a single value
    -
    -        Args:
    -            value (str): value
    -
    -        Returns:
    -            str: decrypted value
    -        """
    -        return base64.encode(self.encrypt(value)).decode("ascii")
    -
    -    def _decrypt_value(self, value):
    -        """
    -        decrypt a single value
    -
    -        Args:
    -            value (str): value
    -
    -        Returns:
    -            str: decrypted value
    -        """
    -        return self.decrypt(base64.decode(value))
    -
    -    def _process_config(self, config, mode):
    -        """
    -        process current config according to encryption mode
    -
    -        Args:
    -            config (dict): config dict (can be nested)
    -            mode (EncryptionMode)
    -        """
    -        new_config = {}
    -        for name, value in config.items():
    -            if name.startswith("__") and value is not None:
    -                if mode == EncryptionMode.Decrypt:
    -                    new_config[name.lstrip("__")] = self._decrypt_value(value)
    -                else:
    -                    # preserve __ to know it's an encrypted value
    -                    new_config[name] = self._encrypt_value(value)
    -            elif isinstance(value, dict):
    -                new_config[name] = self._process_config(value, mode)
    -            else:
    -                new_config[name] = value
    -        return new_config
    -
    -    def get(self, instance_name):
    -        """
    -        get instance config
    -
    -        Args:
    -            instance_name (str): instance name
    -
    -        Returns:
    -            dict: instance config as dict
    -        """
    -        config = json.loads(self.read(instance_name))
    -        return self._process_config(config, EncryptionMode.Decrypt)
    -
    -    def save(self, instance_name, config):
    -        """
    -        save instance config
    -
    -        Args:
    -            instance_name (str): name
    -            config (dict): config data, any key that starts with `__` will be encrypted
    -
    -        Returns:
    -            bool: written or not
    -        """
    -        new_config = self._process_config(config, EncryptionMode.Encrypt)
    -        return self.write(instance_name, json.dumps(new_config))
    -
    -
    -class FileSystemStore(EncryptedConfigStore):
    -    """
    -    Filesystem store is an EncryptedConfigStore
    -
    -    It saves the config relative to `config_env.get_store_config("filesystem")`
    -
    -    To store every instance config in a different path, it uses the given `Location`.
    -    """
    -
    -    def __init__(self, location):
    -        """
    -        create a new `FileSystemStore` that stores config at the given location under configured root.
    -
    -        The root directory can be configured, see `jumpscale.core.config`
    -
    -        Args:
    -            location (Location): where config will be stored per instance
    -        """
    -        super(FileSystemStore, self).__init__(location)
    -        self.root = self.config_env.get_store_config("filesystem")["path"]
    -
    -    @property
    -    def config_root(self):
    -        """
    -        get the root directory where all configurations are written
    -
    -        Returns:
    -            str: path
    -        """
    -        return os.path.join(self.root, self.location.path)
    -
    -    def get_instance_root(self, instance_name):
    -        """
    -        get the directory where instance config is written
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            str: path
    -        """
    -        return os.path.join(self.config_root, instance_name)
    -
    -    def get_path(self, instance_name):
    -        """
    -        get the path to data file where instance config is written
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            str: path
    -        """
    -        return os.path.join(self.get_instance_root(instance_name), "data")
    -
    -    def make_path(self, path):
    -        """
    -        to ensure the given path, create it if it does not exist
    -
    -        Args:
    -            path (str): path
    -        """
    -        if not os.path.exists(path):
    -            os.makedirs(os.path.dirname(path), exist_ok=True)
    -            os.mknod(path)
    -
    -    def read(self, instance_name):
    -        """
    -        read config data from the data file
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            str: data
    -        """
    -        path = self.get_path(instance_name)
    -        return read_file_binary(path)
    -
    -    def list_all(self):
    -        """
    -        list all instance names (directories under config root)
    -
    -        Returns:
    -            list: instance/directory names
    -        """
    -        if not os.path.exists(self.config_root):
    -            return []
    -        return os.listdir(self.config_root)
    -
    -    def write(self, instance_name, data):
    -        """
    -        write config data to data file
    -
    -        Args:
    -            instance_name (str): config
    -            data (str): data
    -
    -        Returns:
    -            bool: written or not
    -        """
    -        path = self.get_path(instance_name)
    -        self.make_path(path)
    -        return write_file_binary(path, data.encode())
    -
    -    def delete(self, instance_name):
    -        """
    -        delete instance config directory
    -
    -        Args:
    -            instance_name (str):
    -        """
    -        path = self.get_instance_root(instance_name)
    -        if os.path.exists(path):
    -            rmtree(path)
    -
    -
    -class RedisStore(EncryptedConfigStore):
    -    """
    -    RedisStore store is an EncryptedConfigStore
    -
    -    It saves the data in redis and configuration for redis comes from `config_env.get_store_config("redis")`
    -    """
    -
    -    def __init__(self, location):
    -        """
    -        create a new redis store, the location given will be used to generate keys
    -
    -        this keys will be combined to get/set instance config
    -
    -        Args:
    -            location (Location)
    -        """
    -        super().__init__(location)
    -        redis_config = self.config_env.get_store_config("redis")
    -        self.redis_client = redis.Redis(redis_config["hostname"], redis_config["port"])
    -
    -    def get_key(self, instance_name):
    -        """
    -        get a key for an instance
    -
    -        this will return a dot-separated key derived from current location
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            str: key
    -        """
    -        return ".".join([self.location.name, instance_name])
    -
    -    def read(self, instance_name):
    -        """
    -        read instance config from redis
    -
    -        Args:
    -            instance_name (name): name
    -
    -        Returns:
    -            str: data
    -        """
    -        return self.redis_client.get(self.get_key(instance_name))
    -
    -    def _full_scan(self, pattern):
    -        """
    -        get the full result of a scan command on current redis database by this pattern
    -
    -        Args:
    -            pattern ([type]): [description]
    -        """
    -        keys = []
    -        cursor, values = self.redis_client.scan(0, pattern)
    -
    -        while values:
    -            keys += values
    -            if not cursor:
    -                break
    -            cursor, values = self.redis_client.scan(cursor, pattern)
    -
    -        return keys
    -
    -    def get_location_keys(self):
    -        """
    -        get all keys under current location (scanned)
    -
    -        Returns:
    -            list: a list of keys
    -        """
    -        return self._full_scan(f"{self.location.name}.*")
    -
    -    def get_instance_keys(self, instance_name):
    -        """
    -        get all instance related keys (scanned)
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            list: list of keys
    -        """
    -        return self._full_scan(f"{self.location.name}.{instance_name}*")
    -
    -    def list_all(self):
    -        """
    -        get all names of instances (instance keys)
    -
    -        Returns:
    -            [type]: [description]
    -        """
    -        names = []
    -
    -        keys = self.get_location_keys()
    -        for key in keys:
    -            # remove location name part
    -            name = key.decode().replace(self.location.name, "").lstrip(".")
    -            if "." not in name:
    -                names.append(name)
    -        return names
    -
    -    def write(self, instance_name, data):
    -        """
    -        set data with the corresponding key for this instance
    -
    -        Args:
    -            instance_name (str): name
    -            data (str): data
    -
    -        Returns:
    -            bool: written or not
    -        """
    -        return self.redis_client.set(self.get_key(instance_name), data)
    -
    -    def delete(self, instance_name):
    -        """
    -        delete all instance related keys
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            bool
    -        """
    -        keys = self.get_instance_keys(instance_name)
    -        if keys:
    -            return self.redis_client.delete(*keys)
    -        return True
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class ConfigStore -(*args, **kwargs) -
    -
    -

    the interface every config store should implement:

    -
      -
    • read(instance_name): -reads the data of this instance name
    • -
    • write(instance_name, data): writes the data of this instance
    • -
    • list_all(instance_name): lists all instance names
    • -
    • delete(instance_name): delete instance data
    • -
    -
    -Source code -
    class ConfigStore(ABC):
    -    """
    -    the interface every config store should implement:
    -
    -    - `read(instance_name)`:  reads the data of this instance name
    -    - `write(instance_name, data)`: writes the data of this instance
    -    - `list_all(instance_name)`: lists all instance names
    -    - `delete(instance_name)`: delete instance data
    -    """
    -
    -    @abstractmethod
    -    def read(self, instance_name):
    -        pass
    -
    -    @abstractmethod
    -    def write(self, instance_name, data):
    -        pass
    -
    -    @abstractmethod
    -    def list_all(self):
    -        pass
    -
    -    @abstractmethod
    -    def delete(self, instance_name):
    -        pass
    -
    -

    Ancestors

    -
      -
    • abc.ABC
    • -
    -

    Subclasses

    - -

    Methods

    -
    -
    -def delete(self, instance_name) -
    -
    -
    -
    -Source code -
    @abstractmethod
    -def delete(self, instance_name):
    -    pass
    -
    -
    -
    -def list_all(self) -
    -
    -
    -
    -Source code -
    @abstractmethod
    -def list_all(self):
    -    pass
    -
    -
    -
    -def read(self, instance_name) -
    -
    -
    -
    -Source code -
    @abstractmethod
    -def read(self, instance_name):
    -    pass
    -
    -
    -
    -def write(self, instance_name, data) -
    -
    -
    -
    -Source code -
    @abstractmethod
    -def write(self, instance_name, data):
    -    pass
    -
    -
    -
    -
    -
    -class EncryptedConfigStore -(location) -
    -
    -

    the base class for any config store backend

    -
    -Source code -
    class EncryptedConfigStore(ConfigStore, EncryptionMixin):
    -    """the base class for any config store backend"""
    -
    -    def __init__(self, location):
    -        self.location = location
    -        self.config_env = Environment()
    -        self.priv_key = base64.decode(self.config_env.get_private_key())
    -        self.nacl = NACL(private_key=self.priv_key)
    -        self.public_key = self.nacl.public_key.encode()
    -
    -        if not self.priv_key:
    -            raise InvalidPrivateKey
    -
    -    def _encrypt_value(self, value):
    -        """
    -        encrypt a single value
    -
    -        Args:
    -            value (str): value
    -
    -        Returns:
    -            str: decrypted value
    -        """
    -        return base64.encode(self.encrypt(value)).decode("ascii")
    -
    -    def _decrypt_value(self, value):
    -        """
    -        decrypt a single value
    -
    -        Args:
    -            value (str): value
    -
    -        Returns:
    -            str: decrypted value
    -        """
    -        return self.decrypt(base64.decode(value))
    -
    -    def _process_config(self, config, mode):
    -        """
    -        process current config according to encryption mode
    -
    -        Args:
    -            config (dict): config dict (can be nested)
    -            mode (EncryptionMode)
    -        """
    -        new_config = {}
    -        for name, value in config.items():
    -            if name.startswith("__") and value is not None:
    -                if mode == EncryptionMode.Decrypt:
    -                    new_config[name.lstrip("__")] = self._decrypt_value(value)
    -                else:
    -                    # preserve __ to know it's an encrypted value
    -                    new_config[name] = self._encrypt_value(value)
    -            elif isinstance(value, dict):
    -                new_config[name] = self._process_config(value, mode)
    -            else:
    -                new_config[name] = value
    -        return new_config
    -
    -    def get(self, instance_name):
    -        """
    -        get instance config
    -
    -        Args:
    -            instance_name (str): instance name
    -
    -        Returns:
    -            dict: instance config as dict
    -        """
    -        config = json.loads(self.read(instance_name))
    -        return self._process_config(config, EncryptionMode.Decrypt)
    -
    -    def save(self, instance_name, config):
    -        """
    -        save instance config
    -
    -        Args:
    -            instance_name (str): name
    -            config (dict): config data, any key that starts with `__` will be encrypted
    -
    -        Returns:
    -            bool: written or not
    -        """
    -        new_config = self._process_config(config, EncryptionMode.Encrypt)
    -        return self.write(instance_name, json.dumps(new_config))
    -
    -

    Ancestors

    - -

    Subclasses

    - -

    Methods

    -
    -
    -def get(self, instance_name) -
    -
    -

    get instance config

    -

    Args

    -
    -
    instance_name : str
    -
    instance name
    -
    -

    Returns

    -
    -
    dict
    -
    instance config as dict
    -
    -
    -Source code -
    def get(self, instance_name):
    -    """
    -    get instance config
    -
    -    Args:
    -        instance_name (str): instance name
    -
    -    Returns:
    -        dict: instance config as dict
    -    """
    -    config = json.loads(self.read(instance_name))
    -    return self._process_config(config, EncryptionMode.Decrypt)
    -
    -
    -
    -def save(self, instance_name, config) -
    -
    -

    save instance config

    -

    Args

    -
    -
    instance_name : str
    -
    name
    -
    config : dict
    -
    config data, any key that starts with __ will be encrypted
    -
    -

    Returns

    -
    -
    bool
    -
    written or not
    -
    -
    -Source code -
    def save(self, instance_name, config):
    -    """
    -    save instance config
    -
    -    Args:
    -        instance_name (str): name
    -        config (dict): config data, any key that starts with `__` will be encrypted
    -
    -    Returns:
    -        bool: written or not
    -    """
    -    new_config = self._process_config(config, EncryptionMode.Encrypt)
    -    return self.write(instance_name, json.dumps(new_config))
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class EncryptionMixin -(*args, **kwargs) -
    -
    -

    A mixin that provides encrypt and decrypt methods, which can be used in any store

    -
    -Source code -
    class EncryptionMixin:
    -    """
    -    A mixin that provides encrypt and decrypt methods, which can be used in any store
    -    """
    -
    -    def encrypt(self, data):
    -        """encrypt data
    -
    -        Args:
    -            data (str): input string
    -
    -        Returns:
    -            bytes: encrypted data as byte string
    -        """
    -        if not isinstance(data, bytes):
    -            data = data.encode()
    -        return self.nacl.encrypt(data, self.public_key)
    -
    -    def decrypt(self, data):
    -        """decrypt data
    -
    -        Args:
    -            data (bytes): encrypted byte string
    -
    -        Returns:
    -            str: decrypted data
    -        """
    -        return self.nacl.decrypt(data, self.public_key).decode()
    -
    -

    Subclasses

    - -

    Methods

    -
    -
    -def decrypt(self, data) -
    -
    -

    decrypt data

    -

    Args

    -
    -
    data : bytes
    -
    encrypted byte string
    -
    -

    Returns

    -
    -
    str
    -
    decrypted data
    -
    -
    -Source code -
    def decrypt(self, data):
    -    """decrypt data
    -
    -    Args:
    -        data (bytes): encrypted byte string
    -
    -    Returns:
    -        str: decrypted data
    -    """
    -    return self.nacl.decrypt(data, self.public_key).decode()
    -
    -
    -
    -def encrypt(self, data) -
    -
    -

    encrypt data

    -

    Args

    -
    -
    data : str
    -
    input string
    -
    -

    Returns

    -
    -
    bytes
    -
    encrypted data as byte string
    -
    -
    -Source code -
    def encrypt(self, data):
    -    """encrypt data
    -
    -    Args:
    -        data (str): input string
    -
    -    Returns:
    -        bytes: encrypted data as byte string
    -    """
    -    if not isinstance(data, bytes):
    -        data = data.encode()
    -    return self.nacl.encrypt(data, self.public_key)
    -
    -
    -
    -
    -
    -class EncryptionMode -(*args, **kwargs) -
    -
    -

    An enum to select encryption mode based on loading or storing the data

    -
    -Source code -
    class EncryptionMode(Enum):
    -    """
    -    An enum to select encryption mode based on loading or storing the data
    -    """
    -
    -    Encrypt = 0
    -    Decrypt = 1
    -
    -

    Ancestors

    -
      -
    • enum.Enum
    • -
    -

    Class variables

    -
    -
    var Decrypt
    -
    -
    -
    -
    var Encrypt
    -
    -
    -
    -
    -
    -
    -class FileSystemStore -(location) -
    -
    -

    Filesystem store is an EncryptedConfigStore

    -

    It saves the config relative to config_env.get_store_config("filesystem")

    -

    To store every instance config in a different path, it uses the given Location.

    -

    create a new FileSystemStore that stores config at the given location under configured root.

    -

    The root directory can be configured, see jumpscale.core.config

    -

    Args

    -
    -
    location : Location
    -
    where config will be stored per instance
    -
    -
    -Source code -
    class FileSystemStore(EncryptedConfigStore):
    -    """
    -    Filesystem store is an EncryptedConfigStore
    -
    -    It saves the config relative to `config_env.get_store_config("filesystem")`
    -
    -    To store every instance config in a different path, it uses the given `Location`.
    -    """
    -
    -    def __init__(self, location):
    -        """
    -        create a new `FileSystemStore` that stores config at the given location under configured root.
    -
    -        The root directory can be configured, see `jumpscale.core.config`
    -
    -        Args:
    -            location (Location): where config will be stored per instance
    -        """
    -        super(FileSystemStore, self).__init__(location)
    -        self.root = self.config_env.get_store_config("filesystem")["path"]
    -
    -    @property
    -    def config_root(self):
    -        """
    -        get the root directory where all configurations are written
    -
    -        Returns:
    -            str: path
    -        """
    -        return os.path.join(self.root, self.location.path)
    -
    -    def get_instance_root(self, instance_name):
    -        """
    -        get the directory where instance config is written
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            str: path
    -        """
    -        return os.path.join(self.config_root, instance_name)
    -
    -    def get_path(self, instance_name):
    -        """
    -        get the path to data file where instance config is written
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            str: path
    -        """
    -        return os.path.join(self.get_instance_root(instance_name), "data")
    -
    -    def make_path(self, path):
    -        """
    -        to ensure the given path, create it if it does not exist
    -
    -        Args:
    -            path (str): path
    -        """
    -        if not os.path.exists(path):
    -            os.makedirs(os.path.dirname(path), exist_ok=True)
    -            os.mknod(path)
    -
    -    def read(self, instance_name):
    -        """
    -        read config data from the data file
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            str: data
    -        """
    -        path = self.get_path(instance_name)
    -        return read_file_binary(path)
    -
    -    def list_all(self):
    -        """
    -        list all instance names (directories under config root)
    -
    -        Returns:
    -            list: instance/directory names
    -        """
    -        if not os.path.exists(self.config_root):
    -            return []
    -        return os.listdir(self.config_root)
    -
    -    def write(self, instance_name, data):
    -        """
    -        write config data to data file
    -
    -        Args:
    -            instance_name (str): config
    -            data (str): data
    -
    -        Returns:
    -            bool: written or not
    -        """
    -        path = self.get_path(instance_name)
    -        self.make_path(path)
    -        return write_file_binary(path, data.encode())
    -
    -    def delete(self, instance_name):
    -        """
    -        delete instance config directory
    -
    -        Args:
    -            instance_name (str):
    -        """
    -        path = self.get_instance_root(instance_name)
    -        if os.path.exists(path):
    -            rmtree(path)
    -
    -

    Ancestors

    - -

    Instance variables

    -
    -
    var config_root
    -
    -

    get the root directory where all configurations are written

    -

    Returns

    -
    -
    str
    -
    path
    -
    -
    -Source code -
    @property
    -def config_root(self):
    -    """
    -    get the root directory where all configurations are written
    -
    -    Returns:
    -        str: path
    -    """
    -    return os.path.join(self.root, self.location.path)
    -
    -
    -
    -

    Methods

    -
    -
    -def delete(self, instance_name) -
    -
    -

    delete instance config directory

    -

    Args

    -

    instance_name (str):

    -
    -Source code -
    def delete(self, instance_name):
    -    """
    -    delete instance config directory
    -
    -    Args:
    -        instance_name (str):
    -    """
    -    path = self.get_instance_root(instance_name)
    -    if os.path.exists(path):
    -        rmtree(path)
    -
    -
    -
    -def get_instance_root(self, instance_name) -
    -
    -

    get the directory where instance config is written

    -

    Args

    -
    -
    instance_name : str
    -
    name
    -
    -

    Returns

    -
    -
    str
    -
    path
    -
    -
    -Source code -
    def get_instance_root(self, instance_name):
    -    """
    -    get the directory where instance config is written
    -
    -    Args:
    -        instance_name (str): name
    -
    -    Returns:
    -        str: path
    -    """
    -    return os.path.join(self.config_root, instance_name)
    -
    -
    -
    -def get_path(self, instance_name) -
    -
    -

    get the path to data file where instance config is written

    -

    Args

    -
    -
    instance_name : str
    -
    name
    -
    -

    Returns

    -
    -
    str
    -
    path
    -
    -
    -Source code -
    def get_path(self, instance_name):
    -    """
    -    get the path to data file where instance config is written
    -
    -    Args:
    -        instance_name (str): name
    -
    -    Returns:
    -        str: path
    -    """
    -    return os.path.join(self.get_instance_root(instance_name), "data")
    -
    -
    -
    -def list_all(self) -
    -
    -

    list all instance names (directories under config root)

    -

    Returns

    -
    -
    list
    -
    instance/directory names
    -
    -
    -Source code -
    def list_all(self):
    -    """
    -    list all instance names (directories under config root)
    -
    -    Returns:
    -        list: instance/directory names
    -    """
    -    if not os.path.exists(self.config_root):
    -        return []
    -    return os.listdir(self.config_root)
    -
    -
    -
    -def make_path(self, path) -
    -
    -

    to ensure the given path, create it if it does not exist

    -

    Args

    -
    -
    path : str
    -
    path
    -
    -
    -Source code -
    def make_path(self, path):
    -    """
    -    to ensure the given path, create it if it does not exist
    -
    -    Args:
    -        path (str): path
    -    """
    -    if not os.path.exists(path):
    -        os.makedirs(os.path.dirname(path), exist_ok=True)
    -        os.mknod(path)
    -
    -
    -
    -def read(self, instance_name) -
    -
    -

    read config data from the data file

    -

    Args

    -
    -
    instance_name : str
    -
    name
    -
    -

    Returns

    -
    -
    str
    -
    data
    -
    -
    -Source code -
    def read(self, instance_name):
    -    """
    -    read config data from the data file
    -
    -    Args:
    -        instance_name (str): name
    -
    -    Returns:
    -        str: data
    -    """
    -    path = self.get_path(instance_name)
    -    return read_file_binary(path)
    -
    -
    -
    -def write(self, instance_name, data) -
    -
    -

    write config data to data file

    -

    Args

    -
    -
    instance_name : str
    -
    config
    -
    data : str
    -
    data
    -
    -

    Returns

    -
    -
    bool
    -
    written or not
    -
    -
    -Source code -
    def write(self, instance_name, data):
    -    """
    -    write config data to data file
    -
    -    Args:
    -        instance_name (str): config
    -        data (str): data
    -
    -    Returns:
    -        bool: written or not
    -    """
    -    path = self.get_path(instance_name)
    -    self.make_path(path)
    -    return write_file_binary(path, data.encode())
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class InvalidPrivateKey -(*args, **kwargs) -
    -
    -

    raised when the private key configured is invalid

    -
    -Source code -
    class InvalidPrivateKey(Exception):
    -    """
    -    raised when the private key configured is invalid
    -    """
    -
    -

    Ancestors

    -
      -
    • builtins.Exception
    • -
    • builtins.BaseException
    • -
    -
    -
    -class Location -(*name_list) -
    -
    -

    dot-separated auto-location for any type

    -

    for example, if we have a class in jumpscale/clients/redis/ -location name will be jumpscale.clients.redis.

    -
    -Source code -
    class Location:
    -    """
    -    dot-separated auto-location for any type
    -
    -    for example, if we have a class in jumpscale/clients/redis/<type>
    -    location name will be jumpscale.clients.redis.<type>
    -    """
    -
    -    def __init__(self, *name_list):
    -        self.name_list = list(name_list)
    -
    -    @property
    -    def name(self):
    -        """
    -        get dot seprated string from current name list
    -
    -        Returns:
    -            str: dot separated string
    -        """
    -        return ".".join(self.name_list)
    -
    -    @property
    -    def path(self):
    -        """
    -        get a filesystem path with from `name`, where dots are replaced by `os.sep`
    -
    -        Returns:
    -            str: path
    -        """
    -        return os.path.join(*self.name.split("."))
    -
    -    @classmethod
    -    def from_type(cls, type_):
    -        """
    -        get a location from any type/class
    -
    -        Args:
    -            type_ (type): any type (class)
    -
    -        Returns:
    -            Location: a location object
    -        """
    -        return cls(type_.__module__, type_.__name__)
    -
    -    def __str__(self):
    -        args = "', '".join(self.name_list)
    -        cls_name = self.__class__.__name__
    -        return f"{cls_name}('{args}')"
    -
    -    __repr__ = __str__
    -
    -

    Static methods

    -
    -
    -def from_type(type_) -
    -
    -

    get a location from any type/class

    -

    Args

    -
    -
    type_ : type
    -
    any type (class)
    -
    -

    Returns

    -
    -
    Location
    -
    a location object
    -
    -
    -Source code -
    @classmethod
    -def from_type(cls, type_):
    -    """
    -    get a location from any type/class
    -
    -    Args:
    -        type_ (type): any type (class)
    -
    -    Returns:
    -        Location: a location object
    -    """
    -    return cls(type_.__module__, type_.__name__)
    -
    -
    -
    -

    Instance variables

    -
    -
    var name
    -
    -

    get dot seprated string from current name list

    -

    Returns

    -
    -
    str
    -
    dot separated string
    -
    -
    -Source code -
    @property
    -def name(self):
    -    """
    -    get dot seprated string from current name list
    -
    -    Returns:
    -        str: dot separated string
    -    """
    -    return ".".join(self.name_list)
    -
    -
    -
    var path
    -
    -

    get a filesystem path with from name, where dots are replaced by os.sep

    -

    Returns

    -
    -
    str
    -
    path
    -
    -
    -Source code -
    @property
    -def path(self):
    -    """
    -    get a filesystem path with from `name`, where dots are replaced by `os.sep`
    -
    -    Returns:
    -        str: path
    -    """
    -    return os.path.join(*self.name.split("."))
    -
    -
    -
    -
    -
    -class RedisStore -(location) -
    -
    -

    RedisStore store is an EncryptedConfigStore

    -

    It saves the data in redis and configuration for redis comes from config_env.get_store_config("redis")

    -

    create a new redis store, the location given will be used to generate keys

    -

    this keys will be combined to get/set instance config

    -

    Args

    -

    location (Location)

    -
    -Source code -
    class RedisStore(EncryptedConfigStore):
    -    """
    -    RedisStore store is an EncryptedConfigStore
    -
    -    It saves the data in redis and configuration for redis comes from `config_env.get_store_config("redis")`
    -    """
    -
    -    def __init__(self, location):
    -        """
    -        create a new redis store, the location given will be used to generate keys
    -
    -        this keys will be combined to get/set instance config
    -
    -        Args:
    -            location (Location)
    -        """
    -        super().__init__(location)
    -        redis_config = self.config_env.get_store_config("redis")
    -        self.redis_client = redis.Redis(redis_config["hostname"], redis_config["port"])
    -
    -    def get_key(self, instance_name):
    -        """
    -        get a key for an instance
    -
    -        this will return a dot-separated key derived from current location
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            str: key
    -        """
    -        return ".".join([self.location.name, instance_name])
    -
    -    def read(self, instance_name):
    -        """
    -        read instance config from redis
    -
    -        Args:
    -            instance_name (name): name
    -
    -        Returns:
    -            str: data
    -        """
    -        return self.redis_client.get(self.get_key(instance_name))
    -
    -    def _full_scan(self, pattern):
    -        """
    -        get the full result of a scan command on current redis database by this pattern
    -
    -        Args:
    -            pattern ([type]): [description]
    -        """
    -        keys = []
    -        cursor, values = self.redis_client.scan(0, pattern)
    -
    -        while values:
    -            keys += values
    -            if not cursor:
    -                break
    -            cursor, values = self.redis_client.scan(cursor, pattern)
    -
    -        return keys
    -
    -    def get_location_keys(self):
    -        """
    -        get all keys under current location (scanned)
    -
    -        Returns:
    -            list: a list of keys
    -        """
    -        return self._full_scan(f"{self.location.name}.*")
    -
    -    def get_instance_keys(self, instance_name):
    -        """
    -        get all instance related keys (scanned)
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            list: list of keys
    -        """
    -        return self._full_scan(f"{self.location.name}.{instance_name}*")
    -
    -    def list_all(self):
    -        """
    -        get all names of instances (instance keys)
    -
    -        Returns:
    -            [type]: [description]
    -        """
    -        names = []
    -
    -        keys = self.get_location_keys()
    -        for key in keys:
    -            # remove location name part
    -            name = key.decode().replace(self.location.name, "").lstrip(".")
    -            if "." not in name:
    -                names.append(name)
    -        return names
    -
    -    def write(self, instance_name, data):
    -        """
    -        set data with the corresponding key for this instance
    -
    -        Args:
    -            instance_name (str): name
    -            data (str): data
    -
    -        Returns:
    -            bool: written or not
    -        """
    -        return self.redis_client.set(self.get_key(instance_name), data)
    -
    -    def delete(self, instance_name):
    -        """
    -        delete all instance related keys
    -
    -        Args:
    -            instance_name (str): name
    -
    -        Returns:
    -            bool
    -        """
    -        keys = self.get_instance_keys(instance_name)
    -        if keys:
    -            return self.redis_client.delete(*keys)
    -        return True
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def delete(self, instance_name) -
    -
    -

    delete all instance related keys

    -

    Args

    -
    -
    instance_name : str
    -
    name
    -
    -

    Returns

    -
    -
    bool
    -
     
    -
    -
    -Source code -
    def delete(self, instance_name):
    -    """
    -    delete all instance related keys
    -
    -    Args:
    -        instance_name (str): name
    -
    -    Returns:
    -        bool
    -    """
    -    keys = self.get_instance_keys(instance_name)
    -    if keys:
    -        return self.redis_client.delete(*keys)
    -    return True
    -
    -
    -
    -def get_instance_keys(self, instance_name) -
    -
    -

    get all instance related keys (scanned)

    -

    Args

    -
    -
    instance_name : str
    -
    name
    -
    -

    Returns

    -
    -
    list
    -
    list of keys
    -
    -
    -Source code -
    def get_instance_keys(self, instance_name):
    -    """
    -    get all instance related keys (scanned)
    -
    -    Args:
    -        instance_name (str): name
    -
    -    Returns:
    -        list: list of keys
    -    """
    -    return self._full_scan(f"{self.location.name}.{instance_name}*")
    -
    -
    -
    -def get_key(self, instance_name) -
    -
    -

    get a key for an instance

    -

    this will return a dot-separated key derived from current location

    -

    Args

    -
    -
    instance_name : str
    -
    name
    -
    -

    Returns

    -
    -
    str
    -
    key
    -
    -
    -Source code -
    def get_key(self, instance_name):
    -    """
    -    get a key for an instance
    -
    -    this will return a dot-separated key derived from current location
    -
    -    Args:
    -        instance_name (str): name
    -
    -    Returns:
    -        str: key
    -    """
    -    return ".".join([self.location.name, instance_name])
    -
    -
    -
    -def get_location_keys(self) -
    -
    -

    get all keys under current location (scanned)

    -

    Returns

    -
    -
    list
    -
    a list of keys
    -
    -
    -Source code -
    def get_location_keys(self):
    -    """
    -    get all keys under current location (scanned)
    -
    -    Returns:
    -        list: a list of keys
    -    """
    -    return self._full_scan(f"{self.location.name}.*")
    -
    -
    -
    -def list_all(self) -
    -
    -

    get all names of instances (instance keys)

    -

    Returns

    -
    -Source code -
    def list_all(self):
    -    """
    -    get all names of instances (instance keys)
    -
    -    Returns:
    -        [type]: [description]
    -    """
    -    names = []
    -
    -    keys = self.get_location_keys()
    -    for key in keys:
    -        # remove location name part
    -        name = key.decode().replace(self.location.name, "").lstrip(".")
    -        if "." not in name:
    -            names.append(name)
    -    return names
    -
    -
    -
    -def read(self, instance_name) -
    -
    -

    read instance config from redis

    -

    Args

    -
    -
    instance_name : name
    -
    name
    -
    -

    Returns

    -
    -
    str
    -
    data
    -
    -
    -Source code -
    def read(self, instance_name):
    -    """
    -    read instance config from redis
    -
    -    Args:
    -        instance_name (name): name
    -
    -    Returns:
    -        str: data
    -    """
    -    return self.redis_client.get(self.get_key(instance_name))
    -
    -
    -
    -def write(self, instance_name, data) -
    -
    -

    set data with the corresponding key for this instance

    -

    Args

    -
    -
    instance_name : str
    -
    name
    -
    data : str
    -
    data
    -
    -

    Returns

    -
    -
    bool
    -
    written or not
    -
    -
    -Source code -
    def write(self, instance_name, data):
    -    """
    -    set data with the corresponding key for this instance
    -
    -    Args:
    -        instance_name (str): name
    -        data (str): data
    -
    -    Returns:
    -        bool: written or not
    -    """
    -    return self.redis_client.set(self.get_key(instance_name), data)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/core/config.html b/docs/api/jumpscale/core/config.html deleted file mode 100644 index ef4d69436..000000000 --- a/docs/api/jumpscale/core/config.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - - - -jumpscale.core.config API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.core.config

    -
    -
    -
    -Source code -
    import os
    -import pytoml as toml
    -
    -
    -__all__ = ['config_path', 'js_default_config', 'get_jumpscale_config', 'update_jumpscale_config', 'JumpscaleConfigEnvironment']
    -
    -config_path = os.path.expanduser(os.path.join("~/.config", "jumpscale", "config.toml"))
    -
    -def js_default_config():
    -    return {
    -        "debug": True,
    -        "ssh_key_path": "",
    -        "log_to_redis": False,
    -        "log_to_files": True,
    -        "log_level": 15,
    -        "private_key_path": "",
    -        "secure_config_path": os.path.expanduser(os.path.join("~/.config", "jumpscale", "secureconfig")),
    -    }
    -
    -if not os.path.exists(config_path):
    -    os.makedirs(os.path.dirname(config_path), exist_ok=True)
    -    with open(config_path, "w") as f:
    -        toml.dump(js_default_config(), f)
    -
    -def get_jumpscale_config():
    -    with open(config_path, "r") as f:
    -        return toml.load(f)
    -
    -def update_jumpscale_config(data):
    -    with open(config_path, "w") as f:
    -        toml.dump(data, f)
    -
    -
    -class JumpscaleConfigEnvironment:
    -    def get_private_key_path(self):
    -        config = get_jumpscale_config()
    -        private_key_path = config['private_key_path']
    -        return private_key_path
    -    
    -    def get_private_key(self):
    -        return open(self.get_private_key_path(), "rb").read()
    -
    -    def get_secure_config_path(self):
    -        config = get_jumpscale_config()
    -        secure_config_path = config['secure_config_path']
    -        return os.path.expanduser(secure_config_path)
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def get_jumpscale_config() -
    -
    -
    -
    -Source code -
    def get_jumpscale_config():
    -    with open(config_path, "r") as f:
    -        return toml.load(f)
    -
    -
    -
    -def js_default_config() -
    -
    -
    -
    -Source code -
    def js_default_config():
    -    return {
    -        "debug": True,
    -        "ssh_key_path": "",
    -        "log_to_redis": False,
    -        "log_to_files": True,
    -        "log_level": 15,
    -        "private_key_path": "",
    -        "secure_config_path": os.path.expanduser(os.path.join("~/.config", "jumpscale", "secureconfig")),
    -    }
    -
    -
    -
    -def update_jumpscale_config(data) -
    -
    -
    -
    -Source code -
    def update_jumpscale_config(data):
    -    with open(config_path, "w") as f:
    -        toml.dump(data, f)
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class JumpscaleConfigEnvironment -(*args, **kwargs) -
    -
    -
    -
    -Source code -
    class JumpscaleConfigEnvironment:
    -    def get_private_key_path(self):
    -        config = get_jumpscale_config()
    -        private_key_path = config['private_key_path']
    -        return private_key_path
    -    
    -    def get_private_key(self):
    -        return open(self.get_private_key_path(), "rb").read()
    -
    -    def get_secure_config_path(self):
    -        config = get_jumpscale_config()
    -        secure_config_path = config['secure_config_path']
    -        return os.path.expanduser(secure_config_path)
    -
    -

    Methods

    -
    -
    -def get_private_key(self) -
    -
    -
    -
    -Source code -
    def get_private_key(self):
    -    return open(self.get_private_key_path(), "rb").read()
    -
    -
    -
    -def get_private_key_path(self) -
    -
    -
    -
    -Source code -
    def get_private_key_path(self):
    -    config = get_jumpscale_config()
    -    private_key_path = config['private_key_path']
    -    return private_key_path
    -
    -
    -
    -def get_secure_config_path(self) -
    -
    -
    -
    -Source code -
    def get_secure_config_path(self):
    -    config = get_jumpscale_config()
    -    secure_config_path = config['secure_config_path']
    -    return os.path.expanduser(secure_config_path)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/core/config/config.html b/docs/api/jumpscale/core/config/config.html index b0f08b6de..ec3a08e24 100644 --- a/docs/api/jumpscale/core/config/config.html +++ b/docs/api/jumpscale/core/config/config.html @@ -186,7 +186,10 @@

    Get/Set

    "debug": True, "shell": "ptpython", "logging": { - "default": {"enabled": True, "level": 10,}, + "default": { + "enabled": True, + "level": 10, + }, "redis": { "enabled": True, "level": 15, @@ -211,8 +214,9 @@

    Get/Set

    }, "factory": {"always_reload": False}, "store": "filesystem", - "threebot": {"default": "",}, - "explorer": {"default_url": "https://explorer.testnet.grid.tf/explorer",}, + "threebot": { + "default": "", + }, } @@ -237,7 +241,7 @@

    Get/Set

    def get(key, default=None): - """ Retrives value from jumpscale config + """Retrives value from jumpscale config Arguments: key (str): the key you wish to retrieve @@ -248,7 +252,7 @@

    Get/Set

    def set(key, val): - """ Sets value in jumpscale config + """Sets value in jumpscale config Arguments: key (str): the key you wish to update @@ -260,7 +264,7 @@

    Get/Set

    def set_default(key, val): - """ Sets key to value in jumpscale config and returns + """Sets key to value in jumpscale config and returns Arguments: key (str): the key you wish to update @@ -372,7 +376,7 @@

    Arguments

    Expand source code
    def get(key, default=None):
    -    """ Retrives value from jumpscale config
    +    """Retrives value from jumpscale config
     
         Arguments:
             key (str): the key you wish to retrieve
    @@ -440,7 +444,10 @@ 

    Returns

    "debug": True, "shell": "ptpython", "logging": { - "default": {"enabled": True, "level": 10,}, + "default": { + "enabled": True, + "level": 10, + }, "redis": { "enabled": True, "level": 15, @@ -465,8 +472,9 @@

    Returns

    }, "factory": {"always_reload": False}, "store": "filesystem", - "threebot": {"default": "",}, - "explorer": {"default_url": "https://explorer.testnet.grid.tf/explorer",}, + "threebot": { + "default": "", + }, }
    @@ -483,7 +491,7 @@

    Arguments

    Expand source code
    def set(key, val):
    -    """ Sets value in jumpscale config
    +    """Sets value in jumpscale config
     
         Arguments:
             key (str): the key you wish to update
    @@ -509,7 +517,7 @@ 

    Returns

    Expand source code
    def set_default(key, val):
    -    """ Sets key to value in jumpscale config and returns
    +    """Sets key to value in jumpscale config and returns
     
         Arguments:
             key (str): the key you wish to update
    diff --git a/docs/api/jumpscale/core/exceptions.html b/docs/api/jumpscale/core/exceptions.html
    deleted file mode 100644
    index a11f5ead1..000000000
    --- a/docs/api/jumpscale/core/exceptions.html
    +++ /dev/null
    @@ -1,85 +0,0 @@
    -
    -
    -
    -
    -
    -
    -jumpscale.core.exceptions API documentation
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Module jumpscale.core.exceptions

    -
    -
    -
    -Source code -
    class JSException(Exception):
    -    pass
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class JSException -(*args, **kwargs) -
    -
    -

    Common base class for all non-exit exceptions.

    -
    -Source code -
    class JSException(Exception):
    -    pass
    -
    -

    Ancestors

    -
      -
    • builtins.Exception
    • -
    • builtins.BaseException
    • -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/core/logging.html b/docs/api/jumpscale/core/logging.html deleted file mode 100644 index c2767eeb6..000000000 --- a/docs/api/jumpscale/core/logging.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - -jumpscale.core.logging API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.core.logging

    -
    -
    -
    -Source code -
    class JSLogger:
    -    pass
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class JSLogger -(*args, **kwargs) -
    -
    -
    -
    -Source code -
    class JSLogger:
    -    pass
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/data/countries/index.html b/docs/api/jumpscale/data/countries/index.html deleted file mode 100644 index d78689065..000000000 --- a/docs/api/jumpscale/data/countries/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - -jumpscale.data.countries API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.data.countries

    -
    -
    -

    This module helps picking a random country or even listing all countries.

    -
    JS-NG> j.data.countries.names()                                                        
    -['Aruba', 'Afghanistan', 'Angola', 'Anguilla', 'Åland Islands', 'Albania', 'Andorra', '
    -United Arab Emirates', 'Argentina', 'Armenia', 'American Samoa', 'Antarctica', 'French 
    -Southern Territories', 'Antigua and Barbuda', 'Australia', 'Austria', 'Azerbaijan', 'Bu
    -rundi', 'Belgium', 'Benin', 'Bonaire, Sint Eustatius and Saba', 'Burkina Faso', 'Bangla
    -desh', 'Bulgaria', 'Bahrain', 'Bahamas', 'Bosnia and Herzegovina', 'Saint Barthélemy', 
    -'Belarus', 'Belize', 'Bermuda', 'Bolivia, Plurinational State of', 'Brazil', 'Barbados', 'Brunei Darussalam', 'Bhutan', 'Bouvet Island', 'Botswana', 'Central African Republic', 'Canada', 'Cocos (Keeling) Islands', 'Switzerland', 'Chile', 'China', "Côte d'Ivoire", 'Cameroon', 'Congo, The Democratic Republic of the', 'Congo', 'Cook Islands', 'Colombia', 'Comoros', 'Cabo Verde', 'Costa Rica', 'Cuba', 'Curaçao', 'Christmas Island', 'Cayman Islands', 'Cyprus', 'Czechia', 'Germany', 'Djibouti', 'Dominica', 'Denmark', 'Dominican Republic', 'Algeria', 'Ecuador', 'Egypt', 'Eritrea', 'Western Sahara', 'Spain', 'Estonia', 'Ethiopia', 'Finland', 'Fiji', 'Falkland Islands (Malvinas)', 'France', 'Faroe Islands', 'Micronesia, Federated States of', 'Gabon', 'United Kingdom', 'Georgia', 'Guernsey', 'Ghana', 'Gibraltar', 'Guinea', 'Guadeloupe', 'Gambia', 'Guinea-Bissau', 'Equatorial Guinea', 'Greece', 'Grenada', 'Greenland', 'Guatemala', 'French Guiana', 'Guam', 'Guyana', 'Hong Kong', 'Heard Island and McDonald Islands', 'Honduras', 'Croatia', 'Haiti', 'Hungary', 'Indonesia', 'Isle of Man', 'India', 'British Indian Ocean Territory', 'Ireland', 'Iran, Islamic Republic of', 'Iraq', 'Iceland', 'Israel', 'Italy', 'Jamaica', 'Jersey', 'Jordan', 'Japan', 'Kazakhstan', 'Kenya', 'Kyrgyzstan', 'Cambodia', 'Kiribati', 'Saint Kitts and Nevis', 'Korea, Republic of', 'Kuwait', "Lao People's Democratic Republic", 'Lebanon', 'Liberia', 'Libya', 'Saint Lucia', 'Liechtenstein', 'Sri Lanka', 'Lesotho', 'Lithuania', 'Luxembourg', 'Latvia', 'Macao', 'Saint Martin (French part)', 'Morocco', 'Monaco', 'Moldova, Republic of', 'Madagascar', 'Maldives', 'Mexico', 'Marshall Islands', 'North Macedonia', 'Mali', 'Malta', 'Myanmar', 'Montenegro', 'Mongolia', 'Northern Mariana Islands', 'Mozambique', 'Mauritania', 'Montserrat', 'Martinique', 'Mauritius', 'Malawi', 'Malaysia', 'Mayotte', 'Namibia', 'New Caledonia', 'Niger', 'Norfolk Island', 'Nigeria', 'Nicaragua', 'Niue', 'Netherlands', 'Norway', 'Nepal', 'Nauru', 'New Zealand', 'Oman', 'Pakistan', 'Panama', 'Pitcairn', 'Peru', 'Philippines', 'Palau', 'Papua New Guinea', 'Poland', 'Puerto Rico', "Korea, Democratic People's Republic of", 'Portugal', 'Paraguay', 'Palestine, State of', 'French Polynesia', 'Qatar', 'Réunion', 'Romania', 'Russian Federation', 'Rwanda', 'Saudi Arabia', 'Sudan', 'Senegal', 'Singapore', 'South Georgia and the South Sandwich Islands', 'Saint Helena, Ascension and Tristan da Cunha', 'Svalbard and Jan Mayen', 'Solomon Islands', 'Sierra Leone', 'El Salvador', 'San Marino', 'Somalia', 'Saint Pierre and Miquelon', 'Serbia', 'South Sudan', 'Sao Tome and Principe', 'Suriname', 'Slovakia', 'Slovenia', 'Sweden', 'Eswatini', 'Sint Maarten (Dutch part)', 'Seychelles', 'Syrian Arab Republic', 'Turks and Caicos Islands', 'Chad', 'Togo', 'Thailand', 'Tajikistan', 'Tokelau', 'Turkmenistan', 'Timor-Leste', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Tuvalu', 'Taiwan, Province of China', 'Tanzania, United Republic of', 'Uganda', 'Ukraine', 'United States Minor Outlying Islands', 'Uruguay', 'United States', 'Uzbekistan', 'Holy See (Vatican City State)', 'Saint Vincent and the Grenadines', 'Venezuela, Bolivarian Republic of', 'Virgin Islands, British', 'Virgin Islands, U.S.', 'Viet Nam', 'Vanuatu', 'Wallis and Futuna', 'Samoa', 'Yemen', 'South Africa', 'Zambia', 'Zimbabwe']
    -
    -JS-NG> j.data.countries.random_country()                                               
    -'Seychelles'
    -
    -JS-NG> j.data.countries.random_country()                                               
    -'Malawi'
    -
    -
    -Source code -
    """This module helps picking a random country or even listing all countries.
    -```
    -JS-NG> j.data.countries.names()                                                        
    -['Aruba', 'Afghanistan', 'Angola', 'Anguilla', 'Åland Islands', 'Albania', 'Andorra', '
    -United Arab Emirates', 'Argentina', 'Armenia', 'American Samoa', 'Antarctica', 'French 
    -Southern Territories', 'Antigua and Barbuda', 'Australia', 'Austria', 'Azerbaijan', 'Bu
    -rundi', 'Belgium', 'Benin', 'Bonaire, Sint Eustatius and Saba', 'Burkina Faso', 'Bangla
    -desh', 'Bulgaria', 'Bahrain', 'Bahamas', 'Bosnia and Herzegovina', 'Saint Barthélemy', 
    -'Belarus', 'Belize', 'Bermuda', 'Bolivia, Plurinational State of', 'Brazil', 'Barbados', 'Brunei Darussalam', 'Bhutan', 'Bouvet Island', 'Botswana', 'Central African Republic', 'Canada', 'Cocos (Keeling) Islands', 'Switzerland', 'Chile', 'China', "Côte d'Ivoire", 'Cameroon', 'Congo, The Democratic Republic of the', 'Congo', 'Cook Islands', 'Colombia', 'Comoros', 'Cabo Verde', 'Costa Rica', 'Cuba', 'Curaçao', 'Christmas Island', 'Cayman Islands', 'Cyprus', 'Czechia', 'Germany', 'Djibouti', 'Dominica', 'Denmark', 'Dominican Republic', 'Algeria', 'Ecuador', 'Egypt', 'Eritrea', 'Western Sahara', 'Spain', 'Estonia', 'Ethiopia', 'Finland', 'Fiji', 'Falkland Islands (Malvinas)', 'France', 'Faroe Islands', 'Micronesia, Federated States of', 'Gabon', 'United Kingdom', 'Georgia', 'Guernsey', 'Ghana', 'Gibraltar', 'Guinea', 'Guadeloupe', 'Gambia', 'Guinea-Bissau', 'Equatorial Guinea', 'Greece', 'Grenada', 'Greenland', 'Guatemala', 'French Guiana', 'Guam', 'Guyana', 'Hong Kong', 'Heard Island and McDonald Islands', 'Honduras', 'Croatia', 'Haiti', 'Hungary', 'Indonesia', 'Isle of Man', 'India', 'British Indian Ocean Territory', 'Ireland', 'Iran, Islamic Republic of', 'Iraq', 'Iceland', 'Israel', 'Italy', 'Jamaica', 'Jersey', 'Jordan', 'Japan', 'Kazakhstan', 'Kenya', 'Kyrgyzstan', 'Cambodia', 'Kiribati', 'Saint Kitts and Nevis', 'Korea, Republic of', 'Kuwait', "Lao People's Democratic Republic", 'Lebanon', 'Liberia', 'Libya', 'Saint Lucia', 'Liechtenstein', 'Sri Lanka', 'Lesotho', 'Lithuania', 'Luxembourg', 'Latvia', 'Macao', 'Saint Martin (French part)', 'Morocco', 'Monaco', 'Moldova, Republic of', 'Madagascar', 'Maldives', 'Mexico', 'Marshall Islands', 'North Macedonia', 'Mali', 'Malta', 'Myanmar', 'Montenegro', 'Mongolia', 'Northern Mariana Islands', 'Mozambique', 'Mauritania', 'Montserrat', 'Martinique', 'Mauritius', 'Malawi', 'Malaysia', 'Mayotte', 'Namibia', 'New Caledonia', 'Niger', 'Norfolk Island', 'Nigeria', 'Nicaragua', 'Niue', 'Netherlands', 'Norway', 'Nepal', 'Nauru', 'New Zealand', 'Oman', 'Pakistan', 'Panama', 'Pitcairn', 'Peru', 'Philippines', 'Palau', 'Papua New Guinea', 'Poland', 'Puerto Rico', "Korea, Democratic People's Republic of", 'Portugal', 'Paraguay', 'Palestine, State of', 'French Polynesia', 'Qatar', 'Réunion', 'Romania', 'Russian Federation', 'Rwanda', 'Saudi Arabia', 'Sudan', 'Senegal', 'Singapore', 'South Georgia and the South Sandwich Islands', 'Saint Helena, Ascension and Tristan da Cunha', 'Svalbard and Jan Mayen', 'Solomon Islands', 'Sierra Leone', 'El Salvador', 'San Marino', 'Somalia', 'Saint Pierre and Miquelon', 'Serbia', 'South Sudan', 'Sao Tome and Principe', 'Suriname', 'Slovakia', 'Slovenia', 'Sweden', 'Eswatini', 'Sint Maarten (Dutch part)', 'Seychelles', 'Syrian Arab Republic', 'Turks and Caicos Islands', 'Chad', 'Togo', 'Thailand', 'Tajikistan', 'Tokelau', 'Turkmenistan', 'Timor-Leste', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Tuvalu', 'Taiwan, Province of China', 'Tanzania, United Republic of', 'Uganda', 'Ukraine', 'United States Minor Outlying Islands', 'Uruguay', 'United States', 'Uzbekistan', 'Holy See (Vatican City State)', 'Saint Vincent and the Grenadines', 'Venezuela, Bolivarian Republic of', 'Virgin Islands, British', 'Virgin Islands, U.S.', 'Viet Nam', 'Vanuatu', 'Wallis and Futuna', 'Samoa', 'Yemen', 'South Africa', 'Zambia', 'Zimbabwe']
    -
    -JS-NG> j.data.countries.random_country()                                               
    -'Seychelles'
    -
    -JS-NG> j.data.countries.random_country()                                               
    -'Malawi'
    -```
    -"""
    -import pycountry
    -import random
    -
    -
    -def names():
    -    """Generates list of country names."""
    -    return [country.name for country in pycountry.countries]
    -
    -
    -def random_country():
    -    """Generates a random country name"""
    -    return random.choice(names())
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def names() -
    -
    -

    Generates list of country names.

    -
    -Source code -
    def names():
    -    """Generates list of country names."""
    -    return [country.name for country in pycountry.countries]
    -
    -
    -
    -def random_country() -
    -
    -

    Generates a random country name

    -
    -Source code -
    def random_country():
    -    """Generates a random country name"""
    -    return random.choice(names())
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/data/encryption/encryption.html b/docs/api/jumpscale/data/encryption/encryption.html deleted file mode 100644 index 8b7878036..000000000 --- a/docs/api/jumpscale/data/encryption/encryption.html +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - -jumpscale.data.encryption.encryption API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.data.encryption.encryption

    -
    -
    -
    -Source code -
    from jumpscale.loader import j
    -from .mnemonic import key_to_mnemonic, mnemonic_to_key, generate_mnemonic
    -
    -
    -def encrypt(message, private_key, receiver_public_key):
    -    """Encrypt the message using the public key of the reciever and the sender's private key.
    -
    -    Args:
    -        message (bytes): The message.
    -        private_key (bytes): sender's private key.
    -        receiver_public_key (bytes): receiver's public key.
    -
    -    Returns:
    -        bytes: The encryption of the message.
    -    """
    -    nacl_obj = j.data.nacl.NACL(private_key=private_key)
    -    message = nacl_obj.encrypt(message, receiver_public_key)
    -    return message
    -
    -
    -def decrypt(message, private_key, sender_public_key):
    -    """Decrypt the message using the private key of the reciever and the sender's public key.
    -
    -    Args:
    -        message (bytes): The message.
    -        private_key (bytes): reciever's private key.
    -        sender_public_key (bytes): sender's public key.
    -
    -    Returns:
    -        bytes: The decryption of the message.
    -    """
    -    nacl_obj = j.data.nacl.NACL(private_key=private_key)
    -    message = nacl_obj.decrypt(message, sender_public_key)
    -    return message
    -
    -
    -def encrypt_symmetric(message, symmetric_key):
    -    """Encrypt the message using the secret key.
    -
    -    Args:
    -        message (bytes): The message.
    -        symmetric_key (bytes): The secret key.
    -
    -    Returns:
    -        bytes: The encryption of the message.
    -    """
    -    nacl_obj = j.data.nacl.NACL(symmetric_key=symmetric_key)
    -    return nacl_obj.encrypt_symmetric(message)
    -
    -
    -def decrypt_symmetric(message, symmetric_key):
    -    """Decrypt the message using the secret key.
    -
    -    Args:
    -        message (bytes): The message.
    -        symmetric_key (bytes): The secret key.
    -
    -    Returns:
    -        bytes: The decryption of the message.
    -    """
    -    nacl_obj = j.data.nacl.NACL(symmetric_key=symmetric_key)
    -    return nacl_obj.decrypt_symmetric(message, symmetric_key)
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def decrypt(message, private_key, sender_public_key) -
    -
    -

    Decrypt the message using the private key of the reciever and the sender's public key.

    -

    Args

    -
    -
    message : bytes
    -
    The message.
    -
    private_key : bytes
    -
    reciever's private key.
    -
    sender_public_key : bytes
    -
    sender's public key.
    -
    -

    Returns

    -
    -
    bytes
    -
    The decryption of the message.
    -
    -
    -Source code -
    def decrypt(message, private_key, sender_public_key):
    -    """Decrypt the message using the private key of the reciever and the sender's public key.
    -
    -    Args:
    -        message (bytes): The message.
    -        private_key (bytes): reciever's private key.
    -        sender_public_key (bytes): sender's public key.
    -
    -    Returns:
    -        bytes: The decryption of the message.
    -    """
    -    nacl_obj = j.data.nacl.NACL(private_key=private_key)
    -    message = nacl_obj.decrypt(message, sender_public_key)
    -    return message
    -
    -
    -
    -def decrypt_symmetric(message, symmetric_key) -
    -
    -

    Decrypt the message using the secret key.

    -

    Args

    -
    -
    message : bytes
    -
    The message.
    -
    symmetric_key : bytes
    -
    The secret key.
    -
    -

    Returns

    -
    -
    bytes
    -
    The decryption of the message.
    -
    -
    -Source code -
    def decrypt_symmetric(message, symmetric_key):
    -    """Decrypt the message using the secret key.
    -
    -    Args:
    -        message (bytes): The message.
    -        symmetric_key (bytes): The secret key.
    -
    -    Returns:
    -        bytes: The decryption of the message.
    -    """
    -    nacl_obj = j.data.nacl.NACL(symmetric_key=symmetric_key)
    -    return nacl_obj.decrypt_symmetric(message, symmetric_key)
    -
    -
    -
    -def encrypt(message, private_key, receiver_public_key) -
    -
    -

    Encrypt the message using the public key of the reciever and the sender's private key.

    -

    Args

    -
    -
    message : bytes
    -
    The message.
    -
    private_key : bytes
    -
    sender's private key.
    -
    receiver_public_key : bytes
    -
    receiver's public key.
    -
    -

    Returns

    -
    -
    bytes
    -
    The encryption of the message.
    -
    -
    -Source code -
    def encrypt(message, private_key, receiver_public_key):
    -    """Encrypt the message using the public key of the reciever and the sender's private key.
    -
    -    Args:
    -        message (bytes): The message.
    -        private_key (bytes): sender's private key.
    -        receiver_public_key (bytes): receiver's public key.
    -
    -    Returns:
    -        bytes: The encryption of the message.
    -    """
    -    nacl_obj = j.data.nacl.NACL(private_key=private_key)
    -    message = nacl_obj.encrypt(message, receiver_public_key)
    -    return message
    -
    -
    -
    -def encrypt_symmetric(message, symmetric_key) -
    -
    -

    Encrypt the message using the secret key.

    -

    Args

    -
    -
    message : bytes
    -
    The message.
    -
    symmetric_key : bytes
    -
    The secret key.
    -
    -

    Returns

    -
    -
    bytes
    -
    The encryption of the message.
    -
    -
    -Source code -
    def encrypt_symmetric(message, symmetric_key):
    -    """Encrypt the message using the secret key.
    -
    -    Args:
    -        message (bytes): The message.
    -        symmetric_key (bytes): The secret key.
    -
    -    Returns:
    -        bytes: The encryption of the message.
    -    """
    -    nacl_obj = j.data.nacl.NACL(symmetric_key=symmetric_key)
    -    return nacl_obj.encrypt_symmetric(message)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/data/hash/HashTool.py b/docs/api/jumpscale/data/hash/HashTool.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/api/jumpscale/data/hash/__init__.py b/docs/api/jumpscale/data/hash/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/api/jumpscale/data/randomnames/generate.html b/docs/api/jumpscale/data/randomnames/generate.html deleted file mode 100644 index fc635e617..000000000 --- a/docs/api/jumpscale/data/randomnames/generate.html +++ /dev/null @@ -1,256 +0,0 @@ - - - - - - -jumpscale.data.randomnames.generate API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.data.randomnames.generate

    -
    -
    -

    This is the code of random names

    -
    -Source code -
    """ This is the code of random names   """
    -
    -from random import choice
    -
    -left_names = [
    -    "albattani",
    -    "allen",
    -    "almeida",
    -    "antonelli",
    -    "agnesi",
    -    "archimedes",
    -    "ardinghelli",
    -    "aryabhata",
    -    "austin",
    -    "babbage",
    -    "banach",
    -    "banzai",
    -    "bardeen",
    -    "bartik",
    -    "bassi",
    -    "beaver",
    -    "bell",
    -    "benz",
    -    "bhabha",
    -    "bhaskara",
    -    "black",
    -    "blackburn",
    -    "blackwell",
    -    "bohr",
    -    "booth",
    -    "borg",
    -    "bose",
    -    "boyd",
    -    "brahmagupta",
    -    "brattain",
    -    "brown",
    -    "burnell",
    -    "buck",
    -    "burnell",
    -    "cannon",
    -    "carson",
    -    "cartwright",
    -    "chandrasekhar",
    -    "chaplygin",
    -    "chatelet",
    -    "chatterjee",
    -    "chebyshev",
    -    "cocks",
    -    "cohen",
    -    "chaum",
    -    "clarke",
    -    "colden",
    -    "cori",
    -    "cray",
    -    "wiles",
    -    "williams",
    -    "williamson",
    -    "wilson",
    -    "wing",
    -    "wozniak",
    -    "wright",
    -    "wu",
    -    "yalow",
    -    "yonath",
    -    "zhukovsky",
    -]
    -right_names = [
    -    "admiring",
    -    "adoring",
    -    "affectionate",
    -    "agitated",
    -    "amazing",
    -    "angry",
    -    "awesome",
    -    "blissful",
    -    "bold",
    -    "boring",
    -    "brave",
    -    "charming",
    -    "clever",
    -    "cocky",
    -    "cool",
    -    "compassionate",
    -    "competent",
    -    "condescending",
    -    "confident",
    -    "cranky",
    -    "crazy",
    -    "dazzling",
    -    "determined",
    -    "distracted",
    -    "dreamy",
    -    "eager",
    -    "ecstatic",
    -    "elastic",
    -    "elated",
    -    "elegant",
    -    "eloquent",
    -    "epic",
    -    "fervent",
    -    "festive",
    -    "flamboyant",
    -    "focused",
    -    "friendly",
    -    "frosty",
    -    "gallant",
    -    "gifted",
    -    "goofy",
    -    "gracious",
    -    "happy",
    -    "hardcore",
    -    "heuristic",
    -    "hopeful",
    -    "hungry",
    -    "infallible",
    -    "inspiring",
    -    "jolly",
    -    "jovial",
    -    "keen",
    -    "kind",
    -    "laughing",
    -    "loving",
    -    "lucid",
    -    "magical",
    -    "mystifying",
    -    "modest",
    -    "musing",
    -    "naughty",
    -    "nervous",
    -    "nifty",
    -    "nostalgic",
    -    "objective",
    -    "optimistic",
    -    "peaceful",
    -    "pedantic",
    -    "pensive",
    -    "practical",
    -    "priceless",
    -    "quirky",
    -    "quizzical",
    -    "recursing",
    -    "relaxed",
    -    "reverent",
    -    "romantic",
    -    "sad",
    -    "serene",
    -    "sharp",
    -    "silly",
    -    "sleepy",
    -    "stoic",
    -    "stupefied",
    -    "suspicious",
    -    "sweet",
    -    "tender",
    -    "thirsty",
    -    "trusting",
    -    "unruffled",
    -    "upbeat",
    -    "vibrant",
    -    "vigilant",
    -    "vigorous",
    -    "wizardly",
    -    "wonderful",
    -    "xenodochial",
    -    "youthful",
    -    "zealous",
    -    "zen",
    -]
    -
    -
    -def generate_random_name():
    -    """ Returns a random name "first name" & "last name """
    -
    -    name = "%s_%s" % (choice(right_names), choice(left_names))
    -
    -    return name
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def generate_random_name() -
    -
    -

    Returns a random name "first name" & "last name

    -
    -Source code -
    def generate_random_name():
    -    """ Returns a random name "first name" & "last name """
    -
    -    name = "%s_%s" % (choice(right_names), choice(left_names))
    -
    -    return name
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/data/randomnames/index.html b/docs/api/jumpscale/data/randomnames/index.html deleted file mode 100644 index 9de032bcb..000000000 --- a/docs/api/jumpscale/data/randomnames/index.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - -jumpscale.data.randomnames API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.data.randomnames

    -
    -
    -
    -Source code -
    from .generate import *
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.data.randomnames.generate
    -
    -

    This is the code of random names

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/serializers.html b/docs/api/jumpscale/data/serializers/serializers.html deleted file mode 100644 index 62161b287..000000000 --- a/docs/api/jumpscale/data/serializers/serializers.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - -jumpscale.data.serializers.serializers API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.data.serializers.serializers

    -
    -
    -
    -Source code -
    from json import loads as json_loads, dumps as json_dumps
    -from pytoml import loads as toml_loads, dumps as toml_dumps
    -# from yaml import load as yaml_load, dump as yaml_dump
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/data/terminaltable/terminaltable.html b/docs/api/jumpscale/data/terminaltable/terminaltable.html deleted file mode 100644 index 6cc51bebf..000000000 --- a/docs/api/jumpscale/data/terminaltable/terminaltable.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -jumpscale.data.terminaltable.terminaltable API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.data.terminaltable.terminaltable

    -
    -
    -
    -Source code -
    import terminaltables as tt
    -
    -TABLE_TYPES = {'ascii': tt.AsciiTable, 'single': tt.SingleTable, 'double': tt.DoubleTable}
    -
    -def create(title, data, type_='ascii'):
    -
    -    table_type = TABLE_TYPES.get(type_)
    -    if not table_type:
    -        raise ValueError("invalid type {} allowed types are {}".format(table_type, TABLE_TYPES.keys()))
    -
    -    table = table_type(data, title)
    -
    -    return table.table
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def create(title, data, type_='ascii') -
    -
    -
    -
    -Source code -
    def create(title, data, type_='ascii'):
    -
    -    table_type = TABLE_TYPES.get(type_)
    -    if not table_type:
    -        raise ValueError("invalid type {} allowed types are {}".format(table_type, TABLE_TYPES.keys()))
    -
    -    table = table_type(data, title)
    -
    -    return table.table
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/data/treemanager/exceptions.html b/docs/api/jumpscale/data/treemanager/exceptions.html deleted file mode 100644 index 41f446bbf..000000000 --- a/docs/api/jumpscale/data/treemanager/exceptions.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - -jumpscale.data.treemanager.exceptions API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.data.treemanager.exceptions

    -
    -
    -
    -Source code -
    from jumpscale.core.exceptions import JSException
    -
    -
    -class NameExistsError(JSException):
    -    pass
    -
    -
    -class EmptyNameError(JSException):
    -    pass
    -
    -
    -class RootRemoveError(JSException):
    -    pass
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class EmptyNameError -(message, category=None, level=None, context=None) -
    -
    -

    Common base class for all non-exit exceptions.

    -
    -Source code -
    class EmptyNameError(JSException):
    -    pass
    -
    -

    Ancestors

    -
      -
    • JSException
    • -
    • builtins.Exception
    • -
    • builtins.BaseException
    • -
    -
    -
    -class NameExistsError -(message, category=None, level=None, context=None) -
    -
    -

    Common base class for all non-exit exceptions.

    -
    -Source code -
    class NameExistsError(JSException):
    -    pass
    -
    -

    Ancestors

    -
      -
    • JSException
    • -
    • builtins.Exception
    • -
    • builtins.BaseException
    • -
    -
    -
    -class RootRemoveError -(message, category=None, level=None, context=None) -
    -
    -

    Common base class for all non-exit exceptions.

    -
    -Source code -
    class RootRemoveError(JSException):
    -    pass
    -
    -

    Ancestors

    -
      -
    • JSException
    • -
    • builtins.Exception
    • -
    • builtins.BaseException
    • -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/data/treemanager/index.html b/docs/api/jumpscale/data/treemanager/index.html deleted file mode 100644 index 0f72ebb43..000000000 --- a/docs/api/jumpscale/data/treemanager/index.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - -jumpscale.data.treemanager API documentation - - - - - - - - - -
    - - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/data/treemanager/treemanager.html b/docs/api/jumpscale/data/treemanager/treemanager.html deleted file mode 100644 index 99b463604..000000000 --- a/docs/api/jumpscale/data/treemanager/treemanager.html +++ /dev/null @@ -1,1189 +0,0 @@ - - - - - - -jumpscale.data.treemanager.treemanager API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.data.treemanager.treemanager

    -
    -
    -

    This is a module with a general tree implementation. -A sample usage of the Tree class as a file manager

    -
    if __name__ == "__main__":
    -    tree = Tree()
    -    tree.add_node_by_path("root", {"file_name": "root",
    -                                   "modified": "12/3/2019"})
    -    tree.add_node_by_path("etc", {"file_name": "etc",
    -                                  "modified": "13/3/2018"})
    -    tree.add_node_by_path("etc.hosts", {"file_name": "hosts",
    -                                        "modified": "14/3/2017"})
    -    tree.add_node_by_path("etc.passwd", {"file_name": "passwd",
    -                                         "modified": "14/3/2016"})
    -    pred = lambda x: x.data["modified"].split("/")[-1] < "2018"
    -    too_old = tree.search_custom(pred)
    -    print("Too old files (before 2018):
    -")
    -    for f in too_old:
    -        print(f.name + "
    -")
    -    print("Tree before removing /etc/hosts")
    -    print(tree)
    -    print("Tree after removing /etc/hosts")
    -    tree.remove_node_by_path("etc.hosts")
    -    print(tree)
    -    passwd_file = tree.get_by_path("etc.passwd")
    -    passwd_date = passwd_file.data["modified"]
    -    print("Last time /etc/passwd was modified is: " + passwd_date)
    -
    -
    -Source code -
    """
    -This is a module with a general tree implementation.
    -A sample usage of the Tree class as a file manager
    -```
    -if __name__ == "__main__":
    -    tree = Tree()
    -    tree.add_node_by_path("root", {"file_name": "root",
    -                                   "modified": "12/3/2019"})
    -    tree.add_node_by_path("etc", {"file_name": "etc",
    -                                  "modified": "13/3/2018"})
    -    tree.add_node_by_path("etc.hosts", {"file_name": "hosts",
    -                                        "modified": "14/3/2017"})
    -    tree.add_node_by_path("etc.passwd", {"file_name": "passwd",
    -                                         "modified": "14/3/2016"})
    -    pred = lambda x: x.data["modified"].split("/")[-1] < "2018"
    -    too_old = tree.search_custom(pred)
    -    print("Too old files (before 2018):\n")
    -    for f in too_old:
    -        print(f.name + "\n")
    -    print("Tree before removing /etc/hosts")
    -    print(tree)
    -    print("Tree after removing /etc/hosts")
    -    tree.remove_node_by_path("etc.hosts")
    -    print(tree)
    -    passwd_file = tree.get_by_path("etc.passwd")
    -    passwd_date = passwd_file.data["modified"]
    -    print("Last time /etc/passwd was modified is: " + passwd_date)
    -```
    -"""
    -from .exceptions import NameExistsError, EmptyNameError, RootRemoveError
    -
    -
    -class TreeNode:
    -    def __init__(self, name, parent, data=None):
    -        """
    -        name     (str)               : The name associated with the node
    -        children (dict[str:TreeNode]): A mapping between names and child nodes
    -        parent   (TreeNode or None)  : The parent TreeNode (None for the root)
    -        data                         : Data associated with the node
    -        """
    -        self.name = name
    -        self.parent = parent
    -        self.data = data
    -        self.children = {}
    -
    -    def add_child(self, node):
    -        """Adds a new child
    -
    -        Args:
    -            node (TreeNode): The node to be added
    -
    -        Returns:
    -            TreeNode: The newly added node
    -        """
    -        child_name = node.name
    -        if child_name in self.children:
    -            raise NameExistsError("A child with the given name already exists")
    -        self.children[child_name] = node
    -        return node
    -
    -    def search_by_name(self, name):
    -        """Search in the node's subtree for nodes with the given name
    -
    -        Args:
    -            name (str): The name to be searched for
    -
    -        Returns:
    -            list of TreeNode: The found nodes
    -        """
    -        return self.search_custom(lambda x: x.name == name)
    -
    -    def search_by_data(self, data):
    -        """Search in the node's subtree for nodes with the given data
    -
    -        Args:
    -            data: The data to be searched for
    -
    -        Returns:
    -            list of TreeNode: The found nodes
    -        """
    -        return self.search_custom(lambda x: x.data == data)
    -
    -    def search_custom(self, func):
    -        """Search the node's subtree the nodes satisfying the given predicate
    -
    -        Args:
    -            func (function): A predicate the recieves a TreeNode
    -
    -        Returns:
    -            list of TreeNode: The nodes found
    -        """
    -        result = []
    -        for v in self.children.values():
    -            result.extend(v.search_custom(func))
    -
    -        if self.name != "" and func(self):
    -            result.append(self)
    -        return result
    -
    -    def get_child_by_name(self, name):
    -        """Get the child with the given name
    -
    -        Args:
    -            name (str): The name of the child
    -
    -        Returns:
    -            TreeNode: The reqiested child. None if it doesn't exist.
    -        """
    -        return self.children.get(name)
    -
    -    def remove_child(self, node):
    -        """Remove the node from the children if it exists
    -
    -        Args:
    -            node (TreeNode): The node to be deleted
    -
    -        Returns:
    -            TreeNode: The deleted node
    -        """
    -        return self.remove_child_by_name(node.name)
    -
    -    def remove_child_by_name(self, name):
    -        """Remove the node from the children
    -
    -        Args:
    -            node (TreeNode): The node to be deleted
    -
    -        Returns:
    -            TreeNode: The deleted node. None if it doesn't exist
    -        """
    -        if name in self.children:
    -            node = self.children[name]
    -            del self.children[name]
    -            return node
    -
    -    def get_path(self):
    -        """Retrieves the path of the node
    -
    -        Returns:
    -            str: The path
    -        """
    -        if self.name == "":
    -            return ""
    -        parent_path = self.parent.get_path()
    -        if parent_path == "":
    -            return self.name
    -        else:
    -            return parent_path + "." + self.name
    -
    -    def __str__(self, indentation=0):
    -        """Returns a string representing the node's subtree
    -
    -        Args:
    -            indentation (int, optional): The level to which the representation\
    -                                         will be indented. Defaults to 0.
    -
    -        Returns:
    -            str: The tree representation
    -        """
    -        result = "\t" * indentation + self._string_repr() + "\n"
    -        for v in self.children.values():
    -            result += v.__str__(indentation + 1)
    -        return result
    -
    -    def _string_repr(self):
    -        """A helper function to return the node's name and data as a string
    -
    -        Returns:
    -            str: The node's string representation
    -        """
    -        if self.name == "":
    -            return "dummy_root"
    -        else:
    -            return self.name + str(self.data).replace("\n", "\\n")
    -
    -
    -class Tree:
    -    """"
    -    A class to represent a tree
    -    """
    -
    -    def __init__(self):
    -        self.root = TreeNode("", None)
    -
    -    def search_by_data(self, data):
    -        """Search the nodes in the tree with the given data
    -
    -        Args:
    -            func (function): A predicate the recieves a TreeNode
    -
    -        Returns:
    -            list of TreeNode: The nodes found
    -        """
    -        return self.root.search_by_data(data)
    -
    -    def search_by_name(self, name):
    -        """Search the nodes in the tree with the passed name
    -
    -        Args:
    -            func (function): A predicate the recieves a TreeNode
    -
    -        Returns:
    -            list of TreeNode: The nodes found
    -        """
    -        return self.root.search_by_name(name)
    -
    -    def search_custom(self, func):
    -        """Search the nodes in the tree satisfying the given predicate
    -
    -        Args:
    -            func (function): A predicate the recieves a TreeNode
    -
    -        Returns:
    -            list of TreeNode: The nodes found
    -        """
    -        return self.root.search_custom(func)
    -
    -    def get_by_path(self, path):
    -        """Retrieves a node designated by the given path
    -
    -        Args:
    -            path (str): A string of names separated by a '.' that reaches\
    -             the desired node when followed
    -
    -            data: The data associated with the newly added node
    -
    -        Returns:
    -            None if an intermidiate node is not found.\
    -            Else the searched node is returned
    -        """
    -        path_arr = path.split(".")
    -        current_node = self.root
    -        for name in path_arr:
    -            next_node = current_node.get_child_by_name(name)
    -            if next_node is None:
    -                return None
    -            current_node = next_node
    -        return current_node
    -
    -    def remove_node(self, node):
    -        """Remove a node from the tree.
    -
    -        Args:
    -            node (TreeNode): The node to be removed
    -        """
    -        if node == self.root:
    -            raise RootRemoveError("Can't remove the root node")
    -        node.parent.remove_child(node)
    -        return node
    -
    -    def add_node_by_path(self, path, data=None):
    -        """Add a node designated by the given path
    -
    -        Args:
    -            path (str): A string of names separated by a '.' that reaches\
    -             the desired node when followed
    -
    -            data: The data associated with the newly added node
    -
    -        Notes:
    -            If intermidiate nodes are not found while traversing the path,\
    -            they are created with data=None.
    -        """
    -        path_arr = path.split(".")
    -        current_node = self.root
    -        for path_name in path_arr[:-1]:
    -            if path_name == "":
    -                raise EmptyNameError("Nodes with empty names are not allowed")
    -            next_node = current_node.get_child_by_name(path_name)
    -            if next_node is None:
    -                next_node = TreeNode(path_name, current_node)
    -                current_node.add_child(next_node)
    -            current_node = next_node
    -        new_node = TreeNode(path_arr[-1], current_node, data)
    -        return current_node.add_child(new_node)
    -
    -    def remove_node_by_path(self, path):
    -        """Remove a node designated by the given path
    -
    -        Args:
    -            path (str): A string of names separated by a '.' that reaches\
    -             the desired node when followed
    -        """
    -        path_arr = path.split(".")
    -        current_node = self.root
    -        parent_node = None
    -        for path_name in path_arr:
    -            next_node = current_node.get_child_by_name(path_name)
    -            if next_node is None:
    -                return None
    -            parent_node = current_node
    -            current_node = next_node
    -        return parent_node.remove_child(current_node)
    -
    -    def __str__(self):
    -        "Return a string representation of the tree"
    -        return self.root.__str__(0)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Tree -
    -
    -

    " -A class to represent a tree

    -
    -Source code -
    class Tree:
    -    """"
    -    A class to represent a tree
    -    """
    -
    -    def __init__(self):
    -        self.root = TreeNode("", None)
    -
    -    def search_by_data(self, data):
    -        """Search the nodes in the tree with the given data
    -
    -        Args:
    -            func (function): A predicate the recieves a TreeNode
    -
    -        Returns:
    -            list of TreeNode: The nodes found
    -        """
    -        return self.root.search_by_data(data)
    -
    -    def search_by_name(self, name):
    -        """Search the nodes in the tree with the passed name
    -
    -        Args:
    -            func (function): A predicate the recieves a TreeNode
    -
    -        Returns:
    -            list of TreeNode: The nodes found
    -        """
    -        return self.root.search_by_name(name)
    -
    -    def search_custom(self, func):
    -        """Search the nodes in the tree satisfying the given predicate
    -
    -        Args:
    -            func (function): A predicate the recieves a TreeNode
    -
    -        Returns:
    -            list of TreeNode: The nodes found
    -        """
    -        return self.root.search_custom(func)
    -
    -    def get_by_path(self, path):
    -        """Retrieves a node designated by the given path
    -
    -        Args:
    -            path (str): A string of names separated by a '.' that reaches\
    -             the desired node when followed
    -
    -            data: The data associated with the newly added node
    -
    -        Returns:
    -            None if an intermidiate node is not found.\
    -            Else the searched node is returned
    -        """
    -        path_arr = path.split(".")
    -        current_node = self.root
    -        for name in path_arr:
    -            next_node = current_node.get_child_by_name(name)
    -            if next_node is None:
    -                return None
    -            current_node = next_node
    -        return current_node
    -
    -    def remove_node(self, node):
    -        """Remove a node from the tree.
    -
    -        Args:
    -            node (TreeNode): The node to be removed
    -        """
    -        if node == self.root:
    -            raise RootRemoveError("Can't remove the root node")
    -        node.parent.remove_child(node)
    -        return node
    -
    -    def add_node_by_path(self, path, data=None):
    -        """Add a node designated by the given path
    -
    -        Args:
    -            path (str): A string of names separated by a '.' that reaches\
    -             the desired node when followed
    -
    -            data: The data associated with the newly added node
    -
    -        Notes:
    -            If intermidiate nodes are not found while traversing the path,\
    -            they are created with data=None.
    -        """
    -        path_arr = path.split(".")
    -        current_node = self.root
    -        for path_name in path_arr[:-1]:
    -            if path_name == "":
    -                raise EmptyNameError("Nodes with empty names are not allowed")
    -            next_node = current_node.get_child_by_name(path_name)
    -            if next_node is None:
    -                next_node = TreeNode(path_name, current_node)
    -                current_node.add_child(next_node)
    -            current_node = next_node
    -        new_node = TreeNode(path_arr[-1], current_node, data)
    -        return current_node.add_child(new_node)
    -
    -    def remove_node_by_path(self, path):
    -        """Remove a node designated by the given path
    -
    -        Args:
    -            path (str): A string of names separated by a '.' that reaches\
    -             the desired node when followed
    -        """
    -        path_arr = path.split(".")
    -        current_node = self.root
    -        parent_node = None
    -        for path_name in path_arr:
    -            next_node = current_node.get_child_by_name(path_name)
    -            if next_node is None:
    -                return None
    -            parent_node = current_node
    -            current_node = next_node
    -        return parent_node.remove_child(current_node)
    -
    -    def __str__(self):
    -        "Return a string representation of the tree"
    -        return self.root.__str__(0)
    -
    -

    Methods

    -
    -
    -def add_node_by_path(self, path, data=None) -
    -
    -

    Add a node designated by the given path

    -

    Args

    -
    -
    path : str
    -
    A string of names separated by a '.' that reaches -the desired node when followed
    -
    data
    -
    The data associated with the newly added node
    -
    -

    Notes

    -

    If intermidiate nodes are not found while traversing the path, -they are created with data=None.

    -
    -Source code -
    def add_node_by_path(self, path, data=None):
    -    """Add a node designated by the given path
    -
    -    Args:
    -        path (str): A string of names separated by a '.' that reaches\
    -         the desired node when followed
    -
    -        data: The data associated with the newly added node
    -
    -    Notes:
    -        If intermidiate nodes are not found while traversing the path,\
    -        they are created with data=None.
    -    """
    -    path_arr = path.split(".")
    -    current_node = self.root
    -    for path_name in path_arr[:-1]:
    -        if path_name == "":
    -            raise EmptyNameError("Nodes with empty names are not allowed")
    -        next_node = current_node.get_child_by_name(path_name)
    -        if next_node is None:
    -            next_node = TreeNode(path_name, current_node)
    -            current_node.add_child(next_node)
    -        current_node = next_node
    -    new_node = TreeNode(path_arr[-1], current_node, data)
    -    return current_node.add_child(new_node)
    -
    -
    -
    -def get_by_path(self, path) -
    -
    -

    Retrieves a node designated by the given path

    -

    Args

    -
    -
    path : str
    -
    A string of names separated by a '.' that reaches -the desired node when followed
    -
    data
    -
    The data associated with the newly added node
    -
    -

    Returns

    -
    -
    None if an intermidiate node is not found. -Else the searched node is returned
    -
     
    -
    -
    -Source code -
    def get_by_path(self, path):
    -    """Retrieves a node designated by the given path
    -
    -    Args:
    -        path (str): A string of names separated by a '.' that reaches\
    -         the desired node when followed
    -
    -        data: The data associated with the newly added node
    -
    -    Returns:
    -        None if an intermidiate node is not found.\
    -        Else the searched node is returned
    -    """
    -    path_arr = path.split(".")
    -    current_node = self.root
    -    for name in path_arr:
    -        next_node = current_node.get_child_by_name(name)
    -        if next_node is None:
    -            return None
    -        current_node = next_node
    -    return current_node
    -
    -
    -
    -def remove_node(self, node) -
    -
    -

    Remove a node from the tree.

    -

    Args

    -
    -
    node : TreeNode
    -
    The node to be removed
    -
    -
    -Source code -
    def remove_node(self, node):
    -    """Remove a node from the tree.
    -
    -    Args:
    -        node (TreeNode): The node to be removed
    -    """
    -    if node == self.root:
    -        raise RootRemoveError("Can't remove the root node")
    -    node.parent.remove_child(node)
    -    return node
    -
    -
    -
    -def remove_node_by_path(self, path) -
    -
    -

    Remove a node designated by the given path

    -

    Args

    -
    -
    path : str
    -
    A string of names separated by a '.' that reaches -the desired node when followed
    -
    -
    -Source code -
    def remove_node_by_path(self, path):
    -    """Remove a node designated by the given path
    -
    -    Args:
    -        path (str): A string of names separated by a '.' that reaches\
    -         the desired node when followed
    -    """
    -    path_arr = path.split(".")
    -    current_node = self.root
    -    parent_node = None
    -    for path_name in path_arr:
    -        next_node = current_node.get_child_by_name(path_name)
    -        if next_node is None:
    -            return None
    -        parent_node = current_node
    -        current_node = next_node
    -    return parent_node.remove_child(current_node)
    -
    -
    -
    -def search_by_data(self, data) -
    -
    -

    Search the nodes in the tree with the given data

    -

    Args

    -
    -
    func : function
    -
    A predicate the recieves a TreeNode
    -
    -

    Returns

    -
    -
    list of TreeNode: The nodes found
    -
     
    -
    -
    -Source code -
    def search_by_data(self, data):
    -    """Search the nodes in the tree with the given data
    -
    -    Args:
    -        func (function): A predicate the recieves a TreeNode
    -
    -    Returns:
    -        list of TreeNode: The nodes found
    -    """
    -    return self.root.search_by_data(data)
    -
    -
    -
    -def search_by_name(self, name) -
    -
    -

    Search the nodes in the tree with the passed name

    -

    Args

    -
    -
    func : function
    -
    A predicate the recieves a TreeNode
    -
    -

    Returns

    -
    -
    list of TreeNode: The nodes found
    -
     
    -
    -
    -Source code -
    def search_by_name(self, name):
    -    """Search the nodes in the tree with the passed name
    -
    -    Args:
    -        func (function): A predicate the recieves a TreeNode
    -
    -    Returns:
    -        list of TreeNode: The nodes found
    -    """
    -    return self.root.search_by_name(name)
    -
    -
    -
    -def search_custom(self, func) -
    -
    -

    Search the nodes in the tree satisfying the given predicate

    -

    Args

    -
    -
    func : function
    -
    A predicate the recieves a TreeNode
    -
    -

    Returns

    -
    -
    list of TreeNode: The nodes found
    -
     
    -
    -
    -Source code -
    def search_custom(self, func):
    -    """Search the nodes in the tree satisfying the given predicate
    -
    -    Args:
    -        func (function): A predicate the recieves a TreeNode
    -
    -    Returns:
    -        list of TreeNode: The nodes found
    -    """
    -    return self.root.search_custom(func)
    -
    -
    -
    -
    -
    -class TreeNode -(name, parent, data=None) -
    -
    -

    name -(str) -: The name associated with the node -children (dict[str:TreeNode]): A mapping between names and child nodes -parent -(TreeNode or None) -: The parent TreeNode (None for the root) -data -: Data associated with the node

    -
    -Source code -
    class TreeNode:
    -    def __init__(self, name, parent, data=None):
    -        """
    -        name     (str)               : The name associated with the node
    -        children (dict[str:TreeNode]): A mapping between names and child nodes
    -        parent   (TreeNode or None)  : The parent TreeNode (None for the root)
    -        data                         : Data associated with the node
    -        """
    -        self.name = name
    -        self.parent = parent
    -        self.data = data
    -        self.children = {}
    -
    -    def add_child(self, node):
    -        """Adds a new child
    -
    -        Args:
    -            node (TreeNode): The node to be added
    -
    -        Returns:
    -            TreeNode: The newly added node
    -        """
    -        child_name = node.name
    -        if child_name in self.children:
    -            raise NameExistsError("A child with the given name already exists")
    -        self.children[child_name] = node
    -        return node
    -
    -    def search_by_name(self, name):
    -        """Search in the node's subtree for nodes with the given name
    -
    -        Args:
    -            name (str): The name to be searched for
    -
    -        Returns:
    -            list of TreeNode: The found nodes
    -        """
    -        return self.search_custom(lambda x: x.name == name)
    -
    -    def search_by_data(self, data):
    -        """Search in the node's subtree for nodes with the given data
    -
    -        Args:
    -            data: The data to be searched for
    -
    -        Returns:
    -            list of TreeNode: The found nodes
    -        """
    -        return self.search_custom(lambda x: x.data == data)
    -
    -    def search_custom(self, func):
    -        """Search the node's subtree the nodes satisfying the given predicate
    -
    -        Args:
    -            func (function): A predicate the recieves a TreeNode
    -
    -        Returns:
    -            list of TreeNode: The nodes found
    -        """
    -        result = []
    -        for v in self.children.values():
    -            result.extend(v.search_custom(func))
    -
    -        if self.name != "" and func(self):
    -            result.append(self)
    -        return result
    -
    -    def get_child_by_name(self, name):
    -        """Get the child with the given name
    -
    -        Args:
    -            name (str): The name of the child
    -
    -        Returns:
    -            TreeNode: The reqiested child. None if it doesn't exist.
    -        """
    -        return self.children.get(name)
    -
    -    def remove_child(self, node):
    -        """Remove the node from the children if it exists
    -
    -        Args:
    -            node (TreeNode): The node to be deleted
    -
    -        Returns:
    -            TreeNode: The deleted node
    -        """
    -        return self.remove_child_by_name(node.name)
    -
    -    def remove_child_by_name(self, name):
    -        """Remove the node from the children
    -
    -        Args:
    -            node (TreeNode): The node to be deleted
    -
    -        Returns:
    -            TreeNode: The deleted node. None if it doesn't exist
    -        """
    -        if name in self.children:
    -            node = self.children[name]
    -            del self.children[name]
    -            return node
    -
    -    def get_path(self):
    -        """Retrieves the path of the node
    -
    -        Returns:
    -            str: The path
    -        """
    -        if self.name == "":
    -            return ""
    -        parent_path = self.parent.get_path()
    -        if parent_path == "":
    -            return self.name
    -        else:
    -            return parent_path + "." + self.name
    -
    -    def __str__(self, indentation=0):
    -        """Returns a string representing the node's subtree
    -
    -        Args:
    -            indentation (int, optional): The level to which the representation\
    -                                         will be indented. Defaults to 0.
    -
    -        Returns:
    -            str: The tree representation
    -        """
    -        result = "\t" * indentation + self._string_repr() + "\n"
    -        for v in self.children.values():
    -            result += v.__str__(indentation + 1)
    -        return result
    -
    -    def _string_repr(self):
    -        """A helper function to return the node's name and data as a string
    -
    -        Returns:
    -            str: The node's string representation
    -        """
    -        if self.name == "":
    -            return "dummy_root"
    -        else:
    -            return self.name + str(self.data).replace("\n", "\\n")
    -
    -

    Methods

    -
    -
    -def add_child(self, node) -
    -
    -

    Adds a new child

    -

    Args

    -
    -
    node : TreeNode
    -
    The node to be added
    -
    -

    Returns

    -
    -
    TreeNode
    -
    The newly added node
    -
    -
    -Source code -
    def add_child(self, node):
    -    """Adds a new child
    -
    -    Args:
    -        node (TreeNode): The node to be added
    -
    -    Returns:
    -        TreeNode: The newly added node
    -    """
    -    child_name = node.name
    -    if child_name in self.children:
    -        raise NameExistsError("A child with the given name already exists")
    -    self.children[child_name] = node
    -    return node
    -
    -
    -
    -def get_child_by_name(self, name) -
    -
    -

    Get the child with the given name

    -

    Args

    -
    -
    name : str
    -
    The name of the child
    -
    -

    Returns

    -
    -
    TreeNode
    -
    The reqiested child. None if it doesn't exist.
    -
    -
    -Source code -
    def get_child_by_name(self, name):
    -    """Get the child with the given name
    -
    -    Args:
    -        name (str): The name of the child
    -
    -    Returns:
    -        TreeNode: The reqiested child. None if it doesn't exist.
    -    """
    -    return self.children.get(name)
    -
    -
    -
    -def get_path(self) -
    -
    -

    Retrieves the path of the node

    -

    Returns

    -
    -
    str
    -
    The path
    -
    -
    -Source code -
    def get_path(self):
    -    """Retrieves the path of the node
    -
    -    Returns:
    -        str: The path
    -    """
    -    if self.name == "":
    -        return ""
    -    parent_path = self.parent.get_path()
    -    if parent_path == "":
    -        return self.name
    -    else:
    -        return parent_path + "." + self.name
    -
    -
    -
    -def remove_child(self, node) -
    -
    -

    Remove the node from the children if it exists

    -

    Args

    -
    -
    node : TreeNode
    -
    The node to be deleted
    -
    -

    Returns

    -
    -
    TreeNode
    -
    The deleted node
    -
    -
    -Source code -
    def remove_child(self, node):
    -    """Remove the node from the children if it exists
    -
    -    Args:
    -        node (TreeNode): The node to be deleted
    -
    -    Returns:
    -        TreeNode: The deleted node
    -    """
    -    return self.remove_child_by_name(node.name)
    -
    -
    -
    -def remove_child_by_name(self, name) -
    -
    -

    Remove the node from the children

    -

    Args

    -
    -
    node : TreeNode
    -
    The node to be deleted
    -
    -

    Returns

    -
    -
    TreeNode
    -
    The deleted node. None if it doesn't exist
    -
    -
    -Source code -
    def remove_child_by_name(self, name):
    -    """Remove the node from the children
    -
    -    Args:
    -        node (TreeNode): The node to be deleted
    -
    -    Returns:
    -        TreeNode: The deleted node. None if it doesn't exist
    -    """
    -    if name in self.children:
    -        node = self.children[name]
    -        del self.children[name]
    -        return node
    -
    -
    -
    -def search_by_data(self, data) -
    -
    -

    Search in the node's subtree for nodes with the given data

    -

    Args

    -
    -
    data
    -
    The data to be searched for
    -
    -

    Returns

    -
    -
    list of TreeNode: The found nodes
    -
     
    -
    -
    -Source code -
    def search_by_data(self, data):
    -    """Search in the node's subtree for nodes with the given data
    -
    -    Args:
    -        data: The data to be searched for
    -
    -    Returns:
    -        list of TreeNode: The found nodes
    -    """
    -    return self.search_custom(lambda x: x.data == data)
    -
    -
    -
    -def search_by_name(self, name) -
    -
    -

    Search in the node's subtree for nodes with the given name

    -

    Args

    -
    -
    name : str
    -
    The name to be searched for
    -
    -

    Returns

    -
    -
    list of TreeNode: The found nodes
    -
     
    -
    -
    -Source code -
    def search_by_name(self, name):
    -    """Search in the node's subtree for nodes with the given name
    -
    -    Args:
    -        name (str): The name to be searched for
    -
    -    Returns:
    -        list of TreeNode: The found nodes
    -    """
    -    return self.search_custom(lambda x: x.name == name)
    -
    -
    -
    -def search_custom(self, func) -
    -
    -

    Search the node's subtree the nodes satisfying the given predicate

    -

    Args

    -
    -
    func : function
    -
    A predicate the recieves a TreeNode
    -
    -

    Returns

    -
    -
    list of TreeNode: The nodes found
    -
     
    -
    -
    -Source code -
    def search_custom(self, func):
    -    """Search the node's subtree the nodes satisfying the given predicate
    -
    -    Args:
    -        func (function): A predicate the recieves a TreeNode
    -
    -    Returns:
    -        list of TreeNode: The nodes found
    -    """
    -    result = []
    -    for v in self.children.values():
    -        result.extend(v.search_custom(func))
    -
    -    if self.name != "" and func(self):
    -        result.append(self)
    -    return result
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/entry_points/index.html b/docs/api/jumpscale/entry_points/index.html index 17a7e0e7e..ab9747db7 100644 --- a/docs/api/jumpscale/entry_points/index.html +++ b/docs/api/jumpscale/entry_points/index.html @@ -41,10 +41,6 @@

    Sub-modules

    ``` ~> poetry run jsync …

    -
    jumpscale.entry_points.usershell
    -
    -
    -
    @@ -70,7 +66,6 @@

    Index

  • jumpscale.entry_points.jsctl
  • jumpscale.entry_points.jsng
  • jumpscale.entry_points.jsync
  • -
  • jumpscale.entry_points.usershell
  • diff --git a/docs/api/jumpscale/entry_points/usershell.html b/docs/api/jumpscale/entry_points/usershell.html deleted file mode 100644 index 767d0895f..000000000 --- a/docs/api/jumpscale/entry_points/usershell.html +++ /dev/null @@ -1,828 +0,0 @@ - - - - - - -jumpscale.entry_points.usershell API documentation - - - - - - - - - - - -
    -
    -
    -

    Module jumpscale.entry_points.usershell

    -
    -
    -
    - -Expand source code - -
    import os
    -import re
    -import time
    -import sys
    -import traceback
    -import argparse
    -import requests
    -
    -import inspect
    -import cgi
    -
    -from prompt_toolkit import PromptSession
    -from prompt_toolkit.completion import Completion
    -from prompt_toolkit.shortcuts import print_formatted_text
    -from prompt_toolkit.eventloop.async_generator import AsyncGeneratorItem
    -from prompt_toolkit.validation import Validator, ValidationError
    -from prompt_toolkit.styles import Style
    -from prompt_toolkit.formatted_text import HTML
    -from jumpscale import threesdk
    -from jumpscale.threesdk import settings
    -from jumpscale.core.exceptions.exceptions import JSException
    -from jumpscale.clients.docker.docker import DockerClient
    -from jumpscale.threesdk.threebot import ThreeBot, DEFAULT_IMAGE
    -from jumpscale.core.config import get_current_version
    -
    -
    -BASE_CONFIG_DIR = os.path.join(os.environ.get("HOME", "/root"), ".jsng")
    -HISTORY_FILENAME = os.path.join(BASE_CONFIG_DIR, "history.txt")
    -
    -DEFAULT_TOOLBAR_MSG = "Welcome to 3sdk enter info for help"
    -
    -style = Style.from_dict(
    -    {
    -        # User input (default text).
    -        "bottom-toolbar": "#ffffff bg:#333333",
    -        "default": "#aaaaaa",
    -        # Prompt.
    -    }
    -)
    -
    -
    -def get_binary_link():
    -    resp = requests.get("https://api.github.com/repos/threefoldtech/js-ng/releases/latest")
    -    resp = resp.json()
    -    # get versions
    -    download_link = ""
    -    version = resp["tag_name"]
    -    for platform in resp["assets"]:
    -        if sys.platform in platform["name"]:
    -            download_link = platform["browser_download_url"]
    -    return version, download_link
    -
    -
    -def update():
    -    print("checking for updates")
    -    latest_version, binary_link = get_binary_link()
    -    current_version = get_current_version()
    -    if latest_version != current_version:
    -        print(f"version: {latest_version} is available get it from {binary_link}")
    -        return
    -    docker_client = DockerClient()
    -    print("Checking for new docker image")
    -    docker_client.client.images.pull(f"{DEFAULT_IMAGE}:{latest_version}")
    -    print("Starting 3sdk containers")
    -    for container_name in os.listdir(os.path.expanduser("~/.config/jumpscale/containers")):
    -        ThreeBot.delete(container_name)
    -        ThreeBot.install(container_name)
    -
    -
    -def print_error(error):
    -    print_formatted_text(HTML("<ansired>{}</ansired>".format(cgi.html.escape(str(error)))))
    -
    -
    -def partition_line(line):
    -    def replacer(m):
    -        return m.group().replace(" ", "\0").strip("\"'")
    -
    -    result = re.sub(r"""(['"]).*?\1""", replacer, line)
    -    parts = []
    -    for part in result.split():
    -        parts.append(part.replace("\0", " "))
    -    return parts
    -
    -
    -def noexpert_error(error):
    -    reports_location = f"{os.environ.get('HOME', os.environ.get('USERPROFILE', ''))}/sandbox/reports"
    -    error_file_location = f"{reports_location}/jsngreport_{time.strftime('%d%H%M%S')}.log"
    -    if not os.path.exists(reports_location):
    -        os.makedirs(reports_location)
    -    with open(error_file_location, "w") as f:
    -        f.write(str(error))
    -    err_msg = f"""Something went wrong. Please contact support at https://support.grid.tf/
    -Error report file has been created on your machine in this location: {error_file_location}
    -        """
    -    return err_msg
    -
    -
    -class Shell(Validator):
    -    def __init__(self):
    -        self._prompt = PromptSession()
    -        self.mode = None
    -        self.toolbarmsg = DEFAULT_TOOLBAR_MSG
    -
    -    def get_completions_async(self, document, complete_event):
    -        text = document.current_line_before_cursor
    -        parts = partition_line(text)
    -        if not parts:
    -            root = None
    -            more = []
    -        else:
    -            root, more = parts[0], parts[1:]
    -        items = []
    -        if not root or not hasattr(threesdk, root):
    -            style = "bg:ansibrightblue"
    -            items += threesdk.__all__
    -            self.toolbarmsg = DEFAULT_TOOLBAR_MSG
    -        else:
    -            style = "bg:ansigreen"
    -            obj = getattr(threesdk, root)
    -            if not more or not hasattr(obj, more[0]):
    -                # complete object attributes
    -                self.toolbarmsg = threesdk._get_doc_line(obj.__doc__)
    -                for name, member in inspect.getmembers(obj, inspect.isroutine):
    -                    if not name.startswith("_"):
    -                        items.append(name)
    -                text = "" if not more else more[-1]
    -            else:
    -                # complete arguments
    -                func = getattr(obj, more[0])
    -                self.toolbarmsg = threesdk._get_doc_line(func.__doc__)
    -                style = "bg:ansired"
    -                for arg in inspect.getfullargspec(func).args:
    -                    field = arg + "="
    -                    if field in text:
    -                        continue
    -                    items.append(field)
    -                if len(more) > 1:
    -                    text = more[-1]
    -                else:
    -                    text = ""
    -
    -        for item in items:
    -            if not item:
    -                continue
    -            if isinstance(item, Completion):
    -                item.start_position = -len(text)
    -            else:
    -                item = Completion(item, -len(text))
    -            regex = ".*".join(text)
    -            item.style = style
    -            if not text or re.search(regex, item.text):
    -                yield AsyncGeneratorItem(item)
    -
    -    def bottom_toolbar(self):
    -        return [("class:bottom-toolbar", self.toolbarmsg)]
    -
    -    def validate(self, document):
    -        text = document.current_line_before_cursor
    -        if not text:
    -            return
    -        root, *more = text.split(" ")
    -        submodule = getattr(threesdk, root, None)
    -        if not submodule:
    -            raise ValidationError(message=f"No such subcommand {root}")
    -        if not more and callable(submodule):
    -            func = root
    -        elif more:
    -            func = getattr(submodule, more[0], None)
    -            if not func:
    -                raise ValidationError(message=f"{root} has no command called {more[0]}")
    -        else:
    -            raise ValidationError(message="Invalid command")
    -        # TODO: validate args
    -        return
    -
    -    def get_func_kwargs(self, cmd):
    -        parts = partition_line(cmd)
    -        root, extra = parts[0], parts[1:]
    -        module = getattr(threesdk, root)
    -        if inspect.isroutine(module):
    -            return module, self.get_kwargs(module, *extra)
    -        else:
    -            func = getattr(module, extra[0])
    -            return func, self.get_kwargs(func, *extra[1:])
    -
    -    def get_kwargs(self, func, *args):
    -        funcspec = inspect.getfullargspec(func)
    -        kwargs = {}
    -        for arg in args:
    -            key, val = arg.split("=", 1)
    -            isbool = funcspec.annotations.get(key) is bool
    -            if isbool:
    -                if val:
    -                    val = val.lower() in ["y", "yes", "1", "true"]
    -                else:
    -                    val = True
    -            kwargs[key] = val
    -        return kwargs
    -
    -    def execute(self, cmd):
    -        if not cmd.strip():
    -            return
    -        try:
    -            func, kwargs = self.get_func_kwargs(cmd)
    -            func(**kwargs)
    -        except JSException as e:
    -            if not settings.expert:
    -                print_error(str(e))
    -            else:
    -                print_error(traceback.format_exc())
    -        except Exception:
    -            if not settings.expert:
    -                print_error(noexpert_error(traceback.format_exc()))
    -            else:
    -                print_error(traceback.format_exc())
    -
    -    def make_prompt(self):
    -        root = ("class:default", "3sdk>")
    -        while True:
    -            try:
    -                result = self.prompt([root])
    -                self.execute(result)
    -            except (EOFError, KeyboardInterrupt):
    -                sys.exit(0)
    -
    -    def prompt(self, msg):
    -        return self._prompt.prompt(
    -            msg, completer=self, validator=self, style=style, bottom_toolbar=self.bottom_toolbar,
    -        )
    -
    -
    -def run():
    -    parser = argparse.ArgumentParser()
    -    parser.add_argument("--update", action="store_true", help="Update 3sdk")
    -    parser.add_argument("--expert", action="store_true", help="Run 3sdk in expert mode")
    -    args = parser.parse_args()
    -    settings.expert = args.expert
    -
    -    if args.update:
    -        update()
    -    else:
    -        shell = Shell()
    -        shell.make_prompt()
    -
    -
    -if __name__ == "__main__":
    -    run()
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    - -
    -
    -
    - -Expand source code - -
    def get_binary_link():
    -    resp = requests.get("https://api.github.com/repos/threefoldtech/js-ng/releases/latest")
    -    resp = resp.json()
    -    # get versions
    -    download_link = ""
    -    version = resp["tag_name"]
    -    for platform in resp["assets"]:
    -        if sys.platform in platform["name"]:
    -            download_link = platform["browser_download_url"]
    -    return version, download_link
    -
    -
    -
    -def noexpert_error(error) -
    -
    -
    -
    - -Expand source code - -
    def noexpert_error(error):
    -    reports_location = f"{os.environ.get('HOME', os.environ.get('USERPROFILE', ''))}/sandbox/reports"
    -    error_file_location = f"{reports_location}/jsngreport_{time.strftime('%d%H%M%S')}.log"
    -    if not os.path.exists(reports_location):
    -        os.makedirs(reports_location)
    -    with open(error_file_location, "w") as f:
    -        f.write(str(error))
    -    err_msg = f"""Something went wrong. Please contact support at https://support.grid.tf/
    -Error report file has been created on your machine in this location: {error_file_location}
    -        """
    -    return err_msg
    -
    -
    -
    -def partition_line(line) -
    -
    -
    -
    - -Expand source code - -
    def partition_line(line):
    -    def replacer(m):
    -        return m.group().replace(" ", "\0").strip("\"'")
    -
    -    result = re.sub(r"""(['"]).*?\1""", replacer, line)
    -    parts = []
    -    for part in result.split():
    -        parts.append(part.replace("\0", " "))
    -    return parts
    -
    -
    -
    -def print_error(error) -
    -
    -
    -
    - -Expand source code - -
    def print_error(error):
    -    print_formatted_text(HTML("<ansired>{}</ansired>".format(cgi.html.escape(str(error)))))
    -
    -
    -
    -def run() -
    -
    -
    -
    - -Expand source code - -
    def run():
    -    parser = argparse.ArgumentParser()
    -    parser.add_argument("--update", action="store_true", help="Update 3sdk")
    -    parser.add_argument("--expert", action="store_true", help="Run 3sdk in expert mode")
    -    args = parser.parse_args()
    -    settings.expert = args.expert
    -
    -    if args.update:
    -        update()
    -    else:
    -        shell = Shell()
    -        shell.make_prompt()
    -
    -
    -
    -def update() -
    -
    -
    -
    - -Expand source code - -
    def update():
    -    print("checking for updates")
    -    latest_version, binary_link = get_binary_link()
    -    current_version = get_current_version()
    -    if latest_version != current_version:
    -        print(f"version: {latest_version} is available get it from {binary_link}")
    -        return
    -    docker_client = DockerClient()
    -    print("Checking for new docker image")
    -    docker_client.client.images.pull(f"{DEFAULT_IMAGE}:{latest_version}")
    -    print("Starting 3sdk containers")
    -    for container_name in os.listdir(os.path.expanduser("~/.config/jumpscale/containers")):
    -        ThreeBot.delete(container_name)
    -        ThreeBot.install(container_name)
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Shell -
    -
    -

    Abstract base class for an input validator.

    -

    A validator is typically created in one of the following two ways:

    -
      -
    • Either by overriding this class and implementing the validate method.
    • -
    • Or by passing a callable to Validator.from_callable.
    • -
    -

    If the validation takes some time and needs to happen in a background -thread, this can be wrapped in a :class:.ThreadedValidator.

    -
    - -Expand source code - -
    class Shell(Validator):
    -    def __init__(self):
    -        self._prompt = PromptSession()
    -        self.mode = None
    -        self.toolbarmsg = DEFAULT_TOOLBAR_MSG
    -
    -    def get_completions_async(self, document, complete_event):
    -        text = document.current_line_before_cursor
    -        parts = partition_line(text)
    -        if not parts:
    -            root = None
    -            more = []
    -        else:
    -            root, more = parts[0], parts[1:]
    -        items = []
    -        if not root or not hasattr(threesdk, root):
    -            style = "bg:ansibrightblue"
    -            items += threesdk.__all__
    -            self.toolbarmsg = DEFAULT_TOOLBAR_MSG
    -        else:
    -            style = "bg:ansigreen"
    -            obj = getattr(threesdk, root)
    -            if not more or not hasattr(obj, more[0]):
    -                # complete object attributes
    -                self.toolbarmsg = threesdk._get_doc_line(obj.__doc__)
    -                for name, member in inspect.getmembers(obj, inspect.isroutine):
    -                    if not name.startswith("_"):
    -                        items.append(name)
    -                text = "" if not more else more[-1]
    -            else:
    -                # complete arguments
    -                func = getattr(obj, more[0])
    -                self.toolbarmsg = threesdk._get_doc_line(func.__doc__)
    -                style = "bg:ansired"
    -                for arg in inspect.getfullargspec(func).args:
    -                    field = arg + "="
    -                    if field in text:
    -                        continue
    -                    items.append(field)
    -                if len(more) > 1:
    -                    text = more[-1]
    -                else:
    -                    text = ""
    -
    -        for item in items:
    -            if not item:
    -                continue
    -            if isinstance(item, Completion):
    -                item.start_position = -len(text)
    -            else:
    -                item = Completion(item, -len(text))
    -            regex = ".*".join(text)
    -            item.style = style
    -            if not text or re.search(regex, item.text):
    -                yield AsyncGeneratorItem(item)
    -
    -    def bottom_toolbar(self):
    -        return [("class:bottom-toolbar", self.toolbarmsg)]
    -
    -    def validate(self, document):
    -        text = document.current_line_before_cursor
    -        if not text:
    -            return
    -        root, *more = text.split(" ")
    -        submodule = getattr(threesdk, root, None)
    -        if not submodule:
    -            raise ValidationError(message=f"No such subcommand {root}")
    -        if not more and callable(submodule):
    -            func = root
    -        elif more:
    -            func = getattr(submodule, more[0], None)
    -            if not func:
    -                raise ValidationError(message=f"{root} has no command called {more[0]}")
    -        else:
    -            raise ValidationError(message="Invalid command")
    -        # TODO: validate args
    -        return
    -
    -    def get_func_kwargs(self, cmd):
    -        parts = partition_line(cmd)
    -        root, extra = parts[0], parts[1:]
    -        module = getattr(threesdk, root)
    -        if inspect.isroutine(module):
    -            return module, self.get_kwargs(module, *extra)
    -        else:
    -            func = getattr(module, extra[0])
    -            return func, self.get_kwargs(func, *extra[1:])
    -
    -    def get_kwargs(self, func, *args):
    -        funcspec = inspect.getfullargspec(func)
    -        kwargs = {}
    -        for arg in args:
    -            key, val = arg.split("=", 1)
    -            isbool = funcspec.annotations.get(key) is bool
    -            if isbool:
    -                if val:
    -                    val = val.lower() in ["y", "yes", "1", "true"]
    -                else:
    -                    val = True
    -            kwargs[key] = val
    -        return kwargs
    -
    -    def execute(self, cmd):
    -        if not cmd.strip():
    -            return
    -        try:
    -            func, kwargs = self.get_func_kwargs(cmd)
    -            func(**kwargs)
    -        except JSException as e:
    -            if not settings.expert:
    -                print_error(str(e))
    -            else:
    -                print_error(traceback.format_exc())
    -        except Exception:
    -            if not settings.expert:
    -                print_error(noexpert_error(traceback.format_exc()))
    -            else:
    -                print_error(traceback.format_exc())
    -
    -    def make_prompt(self):
    -        root = ("class:default", "3sdk>")
    -        while True:
    -            try:
    -                result = self.prompt([root])
    -                self.execute(result)
    -            except (EOFError, KeyboardInterrupt):
    -                sys.exit(0)
    -
    -    def prompt(self, msg):
    -        return self._prompt.prompt(
    -            msg, completer=self, validator=self, style=style, bottom_toolbar=self.bottom_toolbar,
    -        )
    -
    -

    Ancestors

    -
      -
    • prompt_toolkit.validation.Validator
    • -
    -

    Methods

    -
    -
    -def bottom_toolbar(self) -
    -
    -
    -
    - -Expand source code - -
    def bottom_toolbar(self):
    -    return [("class:bottom-toolbar", self.toolbarmsg)]
    -
    -
    -
    -def execute(self, cmd) -
    -
    -
    -
    - -Expand source code - -
    def execute(self, cmd):
    -    if not cmd.strip():
    -        return
    -    try:
    -        func, kwargs = self.get_func_kwargs(cmd)
    -        func(**kwargs)
    -    except JSException as e:
    -        if not settings.expert:
    -            print_error(str(e))
    -        else:
    -            print_error(traceback.format_exc())
    -    except Exception:
    -        if not settings.expert:
    -            print_error(noexpert_error(traceback.format_exc()))
    -        else:
    -            print_error(traceback.format_exc())
    -
    -
    -
    -def get_completions_async(self, document, complete_event) -
    -
    -
    -
    - -Expand source code - -
    def get_completions_async(self, document, complete_event):
    -    text = document.current_line_before_cursor
    -    parts = partition_line(text)
    -    if not parts:
    -        root = None
    -        more = []
    -    else:
    -        root, more = parts[0], parts[1:]
    -    items = []
    -    if not root or not hasattr(threesdk, root):
    -        style = "bg:ansibrightblue"
    -        items += threesdk.__all__
    -        self.toolbarmsg = DEFAULT_TOOLBAR_MSG
    -    else:
    -        style = "bg:ansigreen"
    -        obj = getattr(threesdk, root)
    -        if not more or not hasattr(obj, more[0]):
    -            # complete object attributes
    -            self.toolbarmsg = threesdk._get_doc_line(obj.__doc__)
    -            for name, member in inspect.getmembers(obj, inspect.isroutine):
    -                if not name.startswith("_"):
    -                    items.append(name)
    -            text = "" if not more else more[-1]
    -        else:
    -            # complete arguments
    -            func = getattr(obj, more[0])
    -            self.toolbarmsg = threesdk._get_doc_line(func.__doc__)
    -            style = "bg:ansired"
    -            for arg in inspect.getfullargspec(func).args:
    -                field = arg + "="
    -                if field in text:
    -                    continue
    -                items.append(field)
    -            if len(more) > 1:
    -                text = more[-1]
    -            else:
    -                text = ""
    -
    -    for item in items:
    -        if not item:
    -            continue
    -        if isinstance(item, Completion):
    -            item.start_position = -len(text)
    -        else:
    -            item = Completion(item, -len(text))
    -        regex = ".*".join(text)
    -        item.style = style
    -        if not text or re.search(regex, item.text):
    -            yield AsyncGeneratorItem(item)
    -
    -
    -
    -def get_func_kwargs(self, cmd) -
    -
    -
    -
    - -Expand source code - -
    def get_func_kwargs(self, cmd):
    -    parts = partition_line(cmd)
    -    root, extra = parts[0], parts[1:]
    -    module = getattr(threesdk, root)
    -    if inspect.isroutine(module):
    -        return module, self.get_kwargs(module, *extra)
    -    else:
    -        func = getattr(module, extra[0])
    -        return func, self.get_kwargs(func, *extra[1:])
    -
    -
    -
    -def get_kwargs(self, func, *args) -
    -
    -
    -
    - -Expand source code - -
    def get_kwargs(self, func, *args):
    -    funcspec = inspect.getfullargspec(func)
    -    kwargs = {}
    -    for arg in args:
    -        key, val = arg.split("=", 1)
    -        isbool = funcspec.annotations.get(key) is bool
    -        if isbool:
    -            if val:
    -                val = val.lower() in ["y", "yes", "1", "true"]
    -            else:
    -                val = True
    -        kwargs[key] = val
    -    return kwargs
    -
    -
    -
    -def make_prompt(self) -
    -
    -
    -
    - -Expand source code - -
    def make_prompt(self):
    -    root = ("class:default", "3sdk>")
    -    while True:
    -        try:
    -            result = self.prompt([root])
    -            self.execute(result)
    -        except (EOFError, KeyboardInterrupt):
    -            sys.exit(0)
    -
    -
    -
    -def prompt(self, msg) -
    -
    -
    -
    - -Expand source code - -
    def prompt(self, msg):
    -    return self._prompt.prompt(
    -        msg, completer=self, validator=self, style=style, bottom_toolbar=self.bottom_toolbar,
    -    )
    -
    -
    -
    -def validate(self, document) -
    -
    -

    Validate the input. -If invalid, this should raise a :class:.ValidationError.

    -

    :param document: :class:~prompt_toolkit.document.Document instance.

    -
    - -Expand source code - -
    def validate(self, document):
    -    text = document.current_line_before_cursor
    -    if not text:
    -        return
    -    root, *more = text.split(" ")
    -    submodule = getattr(threesdk, root, None)
    -    if not submodule:
    -        raise ValidationError(message=f"No such subcommand {root}")
    -    if not more and callable(submodule):
    -        func = root
    -    elif more:
    -        func = getattr(submodule, more[0], None)
    -        if not func:
    -            raise ValidationError(message=f"{root} has no command called {more[0]}")
    -    else:
    -        raise ValidationError(message="Invalid command")
    -    # TODO: validate args
    -    return
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - diff --git a/docs/api/jumpscale/god.html b/docs/api/jumpscale/god.html deleted file mode 100644 index 8df6eae9d..000000000 --- a/docs/api/jumpscale/god.html +++ /dev/null @@ -1,803 +0,0 @@ - - - - - - - - jumpscale.loader API documentation - - - - - - - - - - -
    -
    -
    -

    Module jumpscale.loader

    -
    -
    -

    This module coverts the god object j and its loading process. - the idea is with hierarchy like this

    -
    project1/
    -         /rootnamespace (jumpscale)
    -            /subnamespace1
    -                ... pkg1
    -                ... pkg2
    -            /subnamespace2
    -                ... pkg1
    -                ... pkg2
    -project2/
    -         /rootnamespace (jumpscale)
    -            /subnamespace1
    -                ... pkg1
    -                ... pkg2
    -            /subnamespace2
    -                ... pkg1
    -                ... pkg2
    -
    -
      -
    • we get all the paths of the rootnamespace
    • -
    • we get all the subnamespaces
    • -
    • we get all the inner packages and import all of them (lazily) or load them eagerly but just - once.
    • -
    -

    real example:

    -
    js-ng
    -├── jumpscale   <- root namespace
    -│   ├── clients  <- subnamespace where people can register on
    -│   │   ├── base.py
    -│   │   ├── github   <- package in subnamespace
    -│   │   │   ├── github.py
    -│   │   │   └── __init__.py
    -│   │   └── gogs
    -│   │       ├── gogs.py
    -│   │       └── __init__.py
    -│   ├── core
    -│   │   ├── config.py
    -│   │   ├── exceptions.py
    -│   │   └── logging.py
    -│   ├── data
    -│   │   ├── idgenerator
    -│   │   │   ├── idgenerator.py
    -│   │   │   └── __init__.py
    -│   │   └── serializers
    -│   │       ├── __init__.py
    -│   │       └── serializers.py
    -│   ├── god.py
    -│   ├── sals
    -│   │   └── fs
    -│   │       ├── fs.py
    -│   │       └── __init__.py
    -│   └── tools
    -│       └── keygen
    -│           ├── __init__.py
    -│           └── keygen.py
    -├── README.md
    -└── tests
    -    └── test_loads_j.py
    -js-sdk
    -├── jumpscale
    -│   ├── clients
    -│   │   └── gitlab
    -│   │       ├── gitlab.py
    -│   │       └── __init__.py
    -│   ├── sals
    -│   │   └── zos
    -│   │       ├── __init__.py
    -│   │       └── zos.py
    -│   └── tools
    -├── README.md
    -└── tests
    -    └── test_success.py
    -
    -
    - Source code -
    """This module coverts the god object j and its loading process.
    -the idea is with hierarchy like this
    -```
    -project1/
    -         /rootnamespace (jumpscale)
    -            /subnamespace1
    -                ... pkg1
    -                ... pkg2
    -            /subnamespace2
    -                ... pkg1
    -                ... pkg2
    -project2/
    -         /rootnamespace (jumpscale)
    -            /subnamespace1
    -                ... pkg1
    -                ... pkg2
    -            /subnamespace2
    -                ... pkg1
    -                ... pkg2
    -```
    -- we get all the paths of the `rootnamespace`
    -- we get all the subnamespaces
    -- we get all the inner packages and import all of them (lazily) or load them eagerly but just once.
    -
    -
    -real example:
    -```
    -js-ng
    -├── jumpscale   <- root namespace
    -│   ├── clients  <- subnamespace where people can register on
    -│   │   ├── base.py
    -│   │   ├── github   <- package in subnamespace
    -│   │   │   ├── github.py
    -│   │   │   └── __init__.py
    -│   │   └── gogs
    -│   │       ├── gogs.py
    -│   │       └── __init__.py
    -│   ├── core
    -│   │   ├── config.py
    -│   │   ├── exceptions.py
    -│   │   └── logging.py
    -│   ├── data
    -│   │   ├── idgenerator
    -│   │   │   ├── idgenerator.py
    -│   │   │   └── __init__.py
    -│   │   └── serializers
    -│   │       ├── __init__.py
    -│   │       └── serializers.py
    -│   ├── god.py
    -│   ├── sals
    -│   │   └── fs
    -│   │       ├── fs.py
    -│   │       └── __init__.py
    -│   └── tools
    -│       └── keygen
    -│           ├── __init__.py
    -│           └── keygen.py
    -├── README.md
    -└── tests
    -    └── test_loads_j.py
    -js-sdk
    -├── jumpscale
    -│   ├── clients
    -│   │   └── gitlab
    -│   │       ├── gitlab.py
    -│   │       └── __init__.py
    -│   ├── sals
    -│   │   └── zos
    -│   │       ├── __init__.py
    -│   │       └── zos.py
    -│   └── tools
    -├── README.md
    -└── tests
    -    └── test_success.py
    -```
    -"""
    -
    -
    -import collections
    -import importlib
    -import importlib.util
    -from jumpscale.core.config import get_config
    -import os
    -import pkgutil
    -import sys
    -import traceback
    -from types import SimpleNamespace
    -
    -__all__ = ["j"]
    -
    -
    -class ExportedModule(SimpleNamespace):
    -    def __init__(self, m):
    -        super().__init__()
    -        self._m = m
    -        self._exportedas = None
    -        self._loaded = False
    -
    -    def _load(self):
    -        if not self._loaded:
    -            self._exportedas = self._m.export_module_as()
    -            self._loaded = True
    -
    -    def __dir__(self):
    -        self._load()
    -        return dir(self._exportedas)
    -
    -    def __getattr__(self, name):
    -        self._load()
    -        return getattr(self._exportedas, name)
    -
    -
    -class ExportAsSimpleNamespace(SimpleNamespace):
    -    def __getattr__(self, name):
    -        # print("getting attr: ", name)
    -        if name not in self.__dict__:
    -            raise AttributeError(f"Can't find attr {name}")
    -        attr = self.__dict__[name]
    -        if "export_module_as" in dir(attr):
    -            return attr.export_module_as()
    -        else:
    -            return attr
    -
    -
    -def namespaceify(mapping):
    -    if isinstance(mapping, collections.Mapping) and not isinstance(mapping, ExportAsSimpleNamespace):
    -        for key, value in mapping.items():
    -            mapping[key] = namespaceify(value)
    -        return ExportAsSimpleNamespace(**mapping)
    -    return mapping
    -
    -
    -def loadjsmodules():
    -    import jumpscale
    -
    -    loadeddict = {"jumpscale": {}}
    -    for jsnamespace in jumpscale.__path__:
    -        for root, dirs, _ in os.walk(jsnamespace):
    -            for d in dirs:
    -                if d == "__pycache__":
    -                    continue
    -                if os.path.basename(root) == "jumpscale":
    -                    continue
    -
    -                if os.path.dirname(root) != jsnamespace:
    -                    continue
    -                # print("root: {} d: {}".format(root, d))
    -                rootbase = os.path.basename(root)
    -                loadeddict["jumpscale"].setdefault(rootbase, {})
    -                pkgname = d
    -                if "noload" in pkgname or pkgname.startswith("."):
    -                    continue
    -                importedpkgstr = "jumpscale.{}.{}".format(rootbase, pkgname)
    -                __all__.append(importedpkgstr)
    -                # globals()[importedpkgstr] = lazy_import.lazy_module(importedpkgstr)
    -                try:
    -                    m = importlib.import_module(importedpkgstr)
    -                except Exception as e:
    -                    traceback.print_exception(*sys.exc_info())
    -                    print("[-] {} at {} ".format(e, importedpkgstr))
    -                    continue
    -                else:
    -                    if hasattr(m, "export_module_as"):
    -                        # print("rootbase: ", rootbase, importedpkgstr)
    -                        # print(m.export_module_as)
    -                        expmod = ExportedModule(m)
    -                        expmod.__doc__ = m.__doc__
    -                        loadeddict["jumpscale"][rootbase][pkgname] = expmod
    -                        # loadeddict[importedpkgstr] = m.export_module_as
    -                    else:
    -                        loadeddict["jumpscale"][rootbase][pkgname] = m
    -
    -    return namespaceify(loadeddict)
    -
    -
    -class J:
    -    """
    -        Here we simulate god object `j` by delegating the calls to suitable subnamespace
    -    """
    -
    -    def __init__(self):
    -        self.__loaded = False
    -
    -    def __dir__(self):
    -        self._load()
    -        return list(self.__loaded_simplenamespace.jumpscale.__dict__.keys()) + ["config", "exceptions", "logger"]
    -
    -    @property
    -    def logger(self):
    -        return self.__loaded_simplenamespace.jumpscale.core.logging
    -
    -    @property
    -    def application(self):
    -        return self.__loaded_simplenamespace.jumpscale.core.application
    -
    -    @property
    -    def config(self):
    -        return self.__loaded_simplenamespace.jumpscale.core.config
    -
    -    @property
    -    def exceptions(self):
    -        return self.__loaded_simplenamespace.jumpscale.core.exceptions
    -
    -    def reload(self):
    -        self.__loaded = False
    -        self.__loaded_simplenamespace = None
    -        self._load()
    -
    -    def _load(self):
    -        if not self.__loaded:
    -            # print("loading")
    -            # print("id :", id(self))
    -            self.__loaded_simplenamespace = namespaceify(loadjsmodules())
    -            self.__loaded = True
    -
    -    def __getattr__(self, name):
    -        self._load()
    -        return getattr(self.__loaded_simplenamespace.jumpscale, name)
    -
    -
    -j = J()
    -# jxmods = loadjsmodules()
    -j._load()
    -
    -
    -# register alerthandler as an error handler
    -alerts_config = get_config().get("alerts")
    -if alerts_config and alerts_config.get("enabled", True):
    -    j.tools.errorhandler.register_handler(
    -        handler=j.tools.alerthandler.alert_raise, level=alerts_config.get("level", 40)
    -    )
    -
    -# register global error hook
    -sys.excepthook = j.tools.errorhandler.excepthook
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - - diff --git a/docs/api/jumpscale/index.html b/docs/api/jumpscale/index.html index bbf6a37c3..c6238ffdd 100644 --- a/docs/api/jumpscale/index.html +++ b/docs/api/jumpscale/index.html @@ -58,10 +58,6 @@

    Sub-modules

    This module defines all of js-ng specific shell features …

    -
    jumpscale.threesdk
    -
    -
    -
    jumpscale.tools
    @@ -91,7 +87,6 @@

    Index

  • jumpscale.sals
  • jumpscale.servers
  • jumpscale.shell
  • -
  • jumpscale.threesdk
  • jumpscale.tools
  • diff --git a/docs/api/jumpscale/sals/fs/fs.html b/docs/api/jumpscale/sals/fs/fs.html deleted file mode 100644 index aa6245e01..000000000 --- a/docs/api/jumpscale/sals/fs/fs.html +++ /dev/null @@ -1,2503 +0,0 @@ - - - - - - -jumpscale.sals.fs.fs API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.sals.fs.fs

    -
    -
    -

    TODO docs for fs.

    -
    -Source code -
    """
    -#TODO docs for fs.
    -
    -"""
    -
    -import pathlib
    -import tempfile
    -import os
    -import shutil
    -import stat
    -from distutils import dir_util
    -from typing import List
    -
    -basename = os.path.basename
    -dirname = os.path.dirname
    -common_path = os.path.commonpath
    -common_prefix = os.path.commonprefix
    -norm_path = os.path.normpath
    -norm_case = os.path.normcase
    -get_access_time = os.path.getatime
    -get_modified_time = os.path.getmtime
    -get_creation_time = os.path.getctime
    -sep = os.path.sep
    -is_samefile = os.path.samefile
    -expandvars = os.path.expandvars
    -expanduser = os.path.expanduser
    -realpath = os.path.realpath
    -
    -
    -def home():
    -    return str(pathlib.Path.home())
    -
    -
    -def cwd():
    -    """Return current working directory.
    -
    -    Returns:
    -        str: current directory.
    -    """
    -    return str(pathlib.Path.cwd())
    -
    -
    -def is_dir(path: str) -> bool:
    -    """Checks if path is a dir
    -
    -    :param path: path to check
    -    :type path: str
    -    :return: True if is dir else False
    -    :rtype: bool
    -    """
    -    return pathlib.Path(path).is_dir()
    -
    -
    -def is_file(path: str) -> bool:
    -    """Checks if path is a file
    -
    -    Args:
    -        path (str): path to check if is file
    -
    -    Returns:
    -        bool: True if is file and False otherwise
    -    """
    -    return pathlib.Path(path).is_file()
    -
    -
    -def is_symlink(path: str) -> bool:
    -    """Checks if path symlink
    -
    -    Args:
    -        path (str): path to check if symlink
    -
    -    Returns:
    -        bool: True if symlink False otherwise
    -    """
    -    return pathlib.Path(path).is_symlink()
    -
    -
    -def is_absolute(path: str) -> bool:
    -    """Checks if path is absolute
    -
    -    Returns:
    -        bool: True if absolute
    -    """
    -    return pathlib.Path(path).is_absolute()
    -
    -
    -def is_mount(path: str) -> bool:
    -    """Checks if path is mount
    -
    -    Returns:
    -        bool: True if mount
    -    """
    -    return pathlib.Path(path).is_mount()
    -
    -
    -def is_ascii_file(path: str, checksize=4096) -> bool:
    -    """Checks if file `path` is ascii
    -
    -    Args:
    -        path (str): file path
    -        checksize (int, optional): checksize. Defaults to 4096.
    -
    -    Returns:
    -        bool: True if ascii file
    -    """
    -    # TODO: let's talk about checksize feature.
    -    try:
    -        with open(path, encoding="ascii") as f:
    -            f.read()
    -            return True
    -    except UnicodeDecodeError:
    -        return False
    -
    -
    -def is_empty_dir(path: str) -> bool:
    -    """Checks if path is emptry directory
    -
    -    Args:
    -        path (str): path to check if empty directory
    -
    -    Returns:
    -        bool: True if path is emptry directory
    -    """
    -
    -    try:
    -        g = pathlib.Path(path).iterdir()
    -        next(g)
    -    except StopIteration:
    -        # means we can't get next entry -> dir is empty.
    -        return True
    -    else:
    -        return False
    -
    -
    -is_binary_file = lambda path: not is_ascii_file(path)
    -
    -
    -def is_broken_link(path: str, clean=False) -> bool:
    -    """Checks if path is a broken symlink
    -
    -    Args:
    -        path (str): path to check
    -        clean (bool, optional): remove symlink if broken. Defaults to False.
    -
    -    Raises:
    -        NotImplementedError: [description]
    -
    -    Returns:
    -        bool: True if path is a broken symlink
    -    """
    -    raise NotImplementedError()
    -
    -
    -def stem(path: str) -> str:
    -    """returns the stem of a path (path without parent directory and without extension)
    -    e.g
    -        In [2]: t = j.sals.fs.stem("/tmp/tmp-5383p1GOmMOOwvfi.tpl")
    -
    -        In [3]: t
    -        Out[3]: 'tmp-5383p1GOmMOOwvfi'
    -
    -    Args:
    -        path (str): path we want to get its stem
    -
    -    Returns:
    -        [type]: [description]
    -    """
    -    return pathlib.Path(path).stem
    -
    -
    -def mkdir(path: str, exist_ok=True):
    -    """Makes directory at path
    -
    -    Args:
    -        path (str): path to create dir at
    -        exist_ok (bool, optional): won't fail if directory exists. Defaults to True.
    -
    -    Returns:
    -        [type]: [description]
    -    """
    -    return pathlib.Path(path).mkdir(exist_ok=exist_ok)
    -
    -
    -def mkdirs(path: str, exist_ok=True):
    -    """Creates dir as well as all non exisitng parents in the path
    -
    -    Args:
    -        path (str): path to create dir at
    -        exist_ok (bool, optional): won't fail if directory exists. Defaults to True.
    -    """
    -    return os.makedirs(path, exist_ok=exist_ok)
    -
    -
    -def parent(path: str) -> str:
    -    """Get path's parent
    -
    -    Args:
    -        path (str): path to get its parent
    -
    -    Returns:
    -        str: parent path.
    -    """
    -    return str(pathlib.Path(path).parent)
    -
    -
    -def parents(path: str) -> List[str]:
    -    """Get parents list
    -
    -    e.g
    -    >>> j.sals.fs.parents("/tmp/home/ahmed/myfile.py")
    -    [PosixPath('/tmp/home/ahmed'),
    -    PosixPath('/tmp/home'),
    -    PosixPath('/tmp'),
    -    PosixPath('/')]
    -
    -    Args:
    -        path (str): path to get its parents
    -
    -    Returns:
    -        List[str]: list of parents
    -    """
    -
    -    return list([str(p) for p in pathlib.Path(path).parents])
    -
    -
    -def path_parts(path: str) -> List[str]:
    -    """Convert path to a list of parts
    -    e.g
    -     '/tmp/tmp-5383p1GOmMOOwvfi.tpl' ->  ('/', 'tmp', 'tmp-5383p1GOmMOOwvfi.tpl')
    -    Args:
    -        path (str): path to convert to parts
    -
    -    Returns:
    -        List[str]: path parts.
    -    """
    -    return pathlib.Path(path).parts
    -
    -
    -def exists(path: str) -> bool:
    -    """Checks if path exists
    -
    -    Args:
    -        path (str): path to check for existence
    -
    -    Returns:
    -        bool: True if exists
    -    """
    -    return pathlib.Path(path).exists()
    -
    -
    -def rename(path1: str, path2: str):
    -    """Rename path1 to path2
    -
    -    Args:
    -        path1 (str): source path
    -        path2 (str): dest path
    -
    -    """
    -    return pathlib.Path(path1).rename(path2)
    -
    -
    -def expanduser(path: str) -> str:
    -    """Expands the tilde `~` to username
    -    e.g
    -        j.sals.fs.expanduser("~/work") -> '/home/xmonader/work'
    -    Args:
    -        path (str): path with optionally `~`
    -
    -    Returns:
    -        str: path with tilde `~` resolved.
    -    """
    -    return str(pathlib.Path(path).expanduser())
    -
    -
    -def unlink(path: str):
    -    """unlink path
    -
    -    Args:
    -        path (str): path to unlink
    -
    -
    -    """
    -    return pathlib.Path(path).unlink()
    -
    -
    -def read_text(path: str) -> str:
    -    """read ascii content at `path`
    -
    -    Args:
    -        path (str): ascii file path
    -
    -    Returns:
    -        str: ascii content in path
    -    """
    -    return pathlib.Path(path).read_text()
    -
    -
    -read_ascii = read_file = read_text
    -
    -
    -def read_bytes(path: str) -> bytes:
    -    """read binary content at `path`
    -
    -    Args:
    -        path (str): binary file path
    -
    -    Returns:
    -        bytes: binary content in path
    -    """
    -    return pathlib.Path(path).read_bytes()
    -
    -
    -read_binary = read_file_binary = read_bytes
    -
    -
    -def write_text(path: str, data: str, encoding=None):
    -    """write text `data` to path `path` with encoding
    -
    -    Args:
    -        path (str): path to write to
    -        data (str): ascii content
    -        encoding ([type], optional): encoding. Defaults to None.
    -
    -
    -    """
    -    return pathlib.Path(path).write_text(data, encoding)
    -
    -
    -write_ascii = write_file = write_text
    -
    -
    -def write_bytes(path: str, data: bytes):
    -    """write binary `data` to path `path`
    -
    -    Args:
    -        path (str): path to write to
    -        data (bytes): binary content
    -
    -    """
    -    return pathlib.Path(path).write_bytes(data)
    -
    -
    -write_binary = write_file_binary = write_bytes
    -
    -
    -def touch(path: str):
    -    """create file
    -
    -    Args:
    -        path (str): path to create file
    -
    -    """
    -    return pathlib.Path(path).touch()
    -
    -
    -def get_temp_filename(mode="w+b", buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None) -> str:
    -    """Get temp filename
    -
    -    Args:
    -        mode (str, optional): [description]. Defaults to "w+b".
    -        buffering (int, optional): buffering. Defaults to -1.
    -        encoding ([type], optional): encoding . Defaults to None.
    -        newline ([type], optional):  Defaults to None.
    -        suffix ([type], optional): ending suffix. Defaults to None.
    -        prefix ([type], optional): prefix . Defaults to None.
    -        dir ([type], optional): where to create the file. Defaults to None.
    -
    -    Returns:
    -        [str]: temp filename
    -    """
    -    return tempfile.NamedTemporaryFile(mode, buffering, encoding, newline, suffix, prefix, dir).name
    -
    -
    -def get_temp_dirname(suffix=None, prefix=None, dir=None) -> str:
    -    """Get temp directory name
    -
    -    Args:
    -        suffix ([type], optional): ending suffix. Defaults to None.
    -        prefix ([type], optional): prefix . Defaults to None.
    -        dir ([type], optional): where to create the directory. Defaults to None.
    -
    -
    -    Returns:
    -        str: temp directory name.
    -    """
    -    return tempfile.TemporaryDirectory(suffix, prefix, dir).name
    -
    -
    -NamedTemporaryFile = tempfile.NamedTemporaryFile
    -TempraryDirectory = tempfile.TemporaryDirectory
    -mkdtemp = tempfile.mkdtemp
    -mkstemp = tempfile.mkstemp
    -get_temp_dir = tempfile.gettempdir
    -
    -
    -def parts_to_path(parts: List[str]) -> str:
    -    """Convert list of path parts into a path string
    -
    -    Args:
    -        parts (List[str]): path parts
    -
    -    Returns:
    -        str: joined path parts
    -    """
    -    path = pathlib.Path(parts[0])
    -    for p in parts[1:]:
    -        path = path.joinpath(p)
    -    return str(path)
    -
    -
    -def join_paths(*paths):
    -    return parts_to_path(paths)
    -
    -
    -def rm_emptry_dir(path: str):
    -    """Remove empty directory
    -
    -    Args:
    -        path (str): path to remove.
    -    """
    -    path = pathlib.Path(path)
    -    path.rmdir()
    -
    -
    -def rmtree(path: str):
    -    """Remove directory tree
    -    Args:
    -        path (str): path to remove
    -    """
    -    path = pathlib.Path(path)
    -    if path.is_file() or path.is_symlink():
    -        os.remove(path)
    -    elif path.is_dir():
    -        shutil.rmtree(path)
    -
    -
    -def copy_stat(src: str, dst: str, times=True, perms=True):
    -    """Copy stat of src to dst
    -
    -    Args:
    -        src (str): source path
    -        dst (str): destination
    -        times (bool, optional):  Defaults to True.
    -        perms (bool, optional): permissions Defaults to True.
    -    """
    -    st = os.stat(src)
    -    if hasattr(os, "utime"):
    -        os.utime(dst, (st.st_atime, st.st_mtime))
    -    if hasattr(os, "chmod"):
    -        m = stat.S_IMODE(st.st_mode)
    -        os.chmod(dst, m)
    -
    -
    -def copy_file(src: str, dst: str, times=False, perms=False):
    -    """Copy the file, optionally copying the permission bits (mode) and
    -        last access/modify time. If the destination file exists, it will be
    -        replaced. Raises OSError if the destination is a directory. If the
    -        platform does not have the ability to set the permission or times,
    -        ignore it.
    -        This is shutil.copyfile plus bits of shutil.copymode and
    -        shutil.copystat's implementation.
    -        shutil.copy and shutil.copy2 are not supported but are easy to do.
    -
    -    Args:
    -        src (str): source path
    -        dst (str): destination
    -
    -    """
    -    shutil.copyfile(src, dst)
    -    if times or perms:
    -        copy_stat(src, dst, times, perms)
    -
    -
    -def symlink(src: str, dst: str, overwrite=False):
    -    """Create a symbolic link.
    -
    -    Args:
    -        src (str): Source of link
    -        dst (str): Destination path of link
    -        overwrite (bool, optional): If link exists will delete it. Defaults to False.
    -    """
    -    if overwrite and exists(dst):
    -        os.unlink(dst)
    -
    -    os.symlink(src, dst)
    -
    -
    -copy_tree = dir_util.copy_tree
    -chdir = os.chdir
    -
    -
    -def change_dir(path: str) -> str:
    -    """Change current working directory to `path`
    -
    -    Args:
    -        path (str): path to switch current working directory to
    -
    -    Returns:
    -        str: new current working dir
    -    """
    -    os.chdir(path)
    -    return path
    -
    -
    -def chmod(path: str, mode):
    -    """change file mode for path to mode
    -
    -    Args:
    -        path (str): path
    -        mode (int): file mode
    -
    -    """
    -    return pathlib.Path(path).chmod(mode)
    -
    -
    -def lchmod(path: str, mode):
    -    """change file mode for path to mode (handles links too)
    -
    -    Args:
    -        path (str): path
    -        mode (int): file mode
    -
    -    """
    -    return pathlib.Path(path).lchmod(mode)
    -
    -
    -def stat(path: str):
    -    """Gets stat of path `path`
    -
    -    Args:
    -        path (str): path to get its stat
    -
    -    Returns:
    -        stat_result: returns stat struct.
    -    """
    -
    -    return pathlib.Path(path).stat()
    -
    -
    -def lstat(path: str):
    -    """Gets stat of path `path` (handles links)
    -
    -    Args:
    -        path (str): path to get its stat
    -
    -    Returns:
    -        stat_result: returns stat struct.
    -    """
    -
    -    return pathlib.Path(path).lstat()
    -
    -
    -def resolve(path: str) -> str:
    -    """resolve `.` and `..` in path
    -
    -    Args:
    -        path (str): path with optionally `.` and `..`
    -
    -    Returns:
    -        str: resolved path
    -    """
    -    return pathlib.Path(path).resolve()
    -
    -
    -def extension(path: str, include_dot=True):
    -    """Gets the extension of path
    -    '/home/ahmed/myfile.py' -> `.py` if include_dot else `py`
    -
    -    Args:
    -        path (str): [description]
    -        include_dot (bool, optional): controls whether to include the dot or not. Defaults to True.
    -
    -    Returns:
    -        str: extension
    -    """
    -    splitted = os.path.splitext(path)
    -    ext = ""
    -    if len(splitted) == 1:
    -        return ext
    -
    -    if include_dot:
    -        return splitted[1]
    -    else:
    -        return splitted[1].strip(".")
    -
    -
    -ext = extension
    -
    -
    -def chown():
    -    raise NotImplementedError()
    -
    -
    -def read_link(path):
    -    raise NotImplementedError()
    -
    -
    -def remove_links(path):
    -    raise NotImplementedError()
    -
    -
    -def change_filenames(from_, to, where):
    -    pass
    -
    -
    -def replace_words_in_files(from_, to, where):
    -    pass
    -
    -
    -move = shutil.move
    -
    -
    -def default_filter_fun(entry):
    -    return True
    -
    -
    -def walk(path: str, pat="*", filter_fun=default_filter_fun):
    -    """walk recursively on path
    -    e.g
    -        for el in walk('/tmp', filter_fun=j.sals.fs.is_file) : ..
    -        for el in walk('/tmp', filter_fun=j.sals.fs.is_dir) : ..
    -        for el in walk('/tmp', filter_fun= lambda x: len(x)>4 and (j.sals.fs.is_file(x) or j.sals.fs.is_dir(x)) ) : ..
    -
    -
    -    Args:
    -        path (str): path to walk over
    -        pat (str, optional): pattern to match against. Defaults to "*".
    -        filter_fun (Function, optional): filtering function. Defaults to default_filter_fun which accepts anything.
    -    """
    -    p = pathlib.Path(path)
    -    for entry in p.rglob(pat):
    -        # use rglob instead of glob("**/*")
    -        if filter_fun(entry):
    -            yield str(entry)
    -
    -
    -def walk_non_recursive(path: str, filter_fun=default_filter_fun):
    -    """walks non recursively on path
    -    e.g
    -        for el in walk('/tmp', filter=j.sals.fs.is_file) : ..
    -        for el in walk('/tmp', filter=j.sals.fs.is_dir) : ..
    -        for el in walk('/tmp', filter= lambda x: len(x)>4 and (j.sals.fs.is_file(x) or j.sals.fs.is_dir(x)) ) : ..
    -
    -
    -    Args:
    -        path (str): path to walk over
    -        pat (str, optional): pattern to match against. Defaults to "*".
    -        filter_fun (Function, optional): filtering function. Defaults to default_filter_fun which accepts anything.
    -    """
    -    p = pathlib.Path(path)
    -    for entry in p.iterdir():
    -        if filter_fun(entry):
    -            yield str(entry)
    -
    -
    -def walk_files(path: str, recursive=True):
    -    """
    -    walk over files in path and applies function `fun`
    -    e.g
    -
    -        for el in walk_files('/tmp') : ..
    -
    -    Args:
    -        path (str): path to walk over
    -        recursive (bool, optional): recursive or not. Defaults to True.
    -
    -
    -    """
    -
    -    if recursive:
    -        return walk(path, filter_fun=is_file)
    -    else:
    -        return walk_non_recursive(path, filter_fun=is_file)
    -
    -
    -def walk_dirs(path, recursive=True):
    -    """
    -        walk over directories in path and applies function `fun`
    -    e.g
    -
    -        for el in walk_dirs('/tmp') : ..
    -
    -
    -    Args:
    -        path (str): path to walk over
    -        recursive (bool, optional): recursive or not. Defaults to True.
    -
    -
    -    """
    -    if recursive:
    -        return walk(path, filter_fun=is_dir)
    -    else:
    -        return walk_non_recursive(path, filter_fun=is_dir)
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def change_dir(path) -
    -
    -

    Change current working directory to path

    -

    Args

    -
    -
    path : str
    -
    path to switch current working directory to
    -
    -

    Returns

    -
    -
    str
    -
    new current working dir
    -
    -
    -Source code -
    def change_dir(path: str) -> str:
    -    """Change current working directory to `path`
    -
    -    Args:
    -        path (str): path to switch current working directory to
    -
    -    Returns:
    -        str: new current working dir
    -    """
    -    os.chdir(path)
    -    return path
    -
    -
    -
    -def change_filenames(from_, to, where) -
    -
    -
    -
    -Source code -
    def change_filenames(from_, to, where):
    -    pass
    -
    -
    -
    -def chmod(path, mode) -
    -
    -

    change file mode for path to mode

    -

    Args

    -
    -
    path : str
    -
    path
    -
    mode : int
    -
    file mode
    -
    -
    -Source code -
    def chmod(path: str, mode):
    -    """change file mode for path to mode
    -
    -    Args:
    -        path (str): path
    -        mode (int): file mode
    -
    -    """
    -    return pathlib.Path(path).chmod(mode)
    -
    -
    -
    -def chown() -
    -
    -
    -
    -Source code -
    def chown():
    -    raise NotImplementedError()
    -
    -
    -
    -def copy_file(src, dst, times=False, perms=False) -
    -
    -

    Copy the file, optionally copying the permission bits (mode) and -last access/modify time. If the destination file exists, it will be -replaced. Raises OSError if the destination is a directory. If the -platform does not have the ability to set the permission or times, -ignore it. -This is shutil.copyfile plus bits of shutil.copymode and -shutil.copystat's implementation. -shutil.copy and shutil.copy2 are not supported but are easy to do.

    -

    Args

    -
    -
    src : str
    -
    source path
    -
    dst : str
    -
    destination
    -
    -
    -Source code -
    def copy_file(src: str, dst: str, times=False, perms=False):
    -    """Copy the file, optionally copying the permission bits (mode) and
    -        last access/modify time. If the destination file exists, it will be
    -        replaced. Raises OSError if the destination is a directory. If the
    -        platform does not have the ability to set the permission or times,
    -        ignore it.
    -        This is shutil.copyfile plus bits of shutil.copymode and
    -        shutil.copystat's implementation.
    -        shutil.copy and shutil.copy2 are not supported but are easy to do.
    -
    -    Args:
    -        src (str): source path
    -        dst (str): destination
    -
    -    """
    -    shutil.copyfile(src, dst)
    -    if times or perms:
    -        copy_stat(src, dst, times, perms)
    -
    -
    -
    -def copy_stat(src, dst, times=True, perms=True) -
    -
    -

    Copy stat of src to dst

    -

    Args

    -
    -
    src : str
    -
    source path
    -
    dst : str
    -
    destination
    -
    times : bool, optional
    -
    Defaults to True.
    -
    perms : bool, optional
    -
    permissions Defaults to True.
    -
    -
    -Source code -
    def copy_stat(src: str, dst: str, times=True, perms=True):
    -    """Copy stat of src to dst
    -
    -    Args:
    -        src (str): source path
    -        dst (str): destination
    -        times (bool, optional):  Defaults to True.
    -        perms (bool, optional): permissions Defaults to True.
    -    """
    -    st = os.stat(src)
    -    if hasattr(os, "utime"):
    -        os.utime(dst, (st.st_atime, st.st_mtime))
    -    if hasattr(os, "chmod"):
    -        m = stat.S_IMODE(st.st_mode)
    -        os.chmod(dst, m)
    -
    -
    -
    -def cwd() -
    -
    -

    Return current working directory.

    -

    Returns

    -
    -
    str
    -
    current directory.
    -
    -
    -Source code -
    def cwd():
    -    """Return current working directory.
    -
    -    Returns:
    -        str: current directory.
    -    """
    -    return str(pathlib.Path.cwd())
    -
    -
    -
    -def default_filter_fun(entry) -
    -
    -
    -
    -Source code -
    def default_filter_fun(entry):
    -    return True
    -
    -
    -
    -def exists(path) -
    -
    -

    Checks if path exists

    -

    Args

    -
    -
    path : str
    -
    path to check for existence
    -
    -

    Returns

    -
    -
    bool
    -
    True if exists
    -
    -
    -Source code -
    def exists(path: str) -> bool:
    -    """Checks if path exists
    -
    -    Args:
    -        path (str): path to check for existence
    -
    -    Returns:
    -        bool: True if exists
    -    """
    -    return pathlib.Path(path).exists()
    -
    -
    -
    -def expanduser(path) -
    -
    -

    Expands the tilde ~ to username -e.g -j.sals.fs.expanduser("~/work") -> '/home/xmonader/work'

    -

    Args

    -
    -
    path : str
    -
    path with optionally ~
    -
    -

    Returns

    -
    -
    str
    -
    path with tilde ~ resolved.
    -
    -
    -Source code -
    def expanduser(path: str) -> str:
    -    """Expands the tilde `~` to username
    -    e.g
    -        j.sals.fs.expanduser("~/work") -> '/home/xmonader/work'
    -    Args:
    -        path (str): path with optionally `~`
    -
    -    Returns:
    -        str: path with tilde `~` resolved.
    -    """
    -    return str(pathlib.Path(path).expanduser())
    -
    -
    -
    -def ext(path, include_dot=True) -
    -
    -

    Gets the extension of path -'/home/ahmed/myfile.py' -> .py if include_dot else py

    -

    Args

    -
    -
    path : str
    -
    [description]
    -
    include_dot : bool, optional
    -
    controls whether to include the dot or not. Defaults to True.
    -
    -

    Returns

    -
    -
    str
    -
    extension
    -
    -
    -Source code -
    def extension(path: str, include_dot=True):
    -    """Gets the extension of path
    -    '/home/ahmed/myfile.py' -> `.py` if include_dot else `py`
    -
    -    Args:
    -        path (str): [description]
    -        include_dot (bool, optional): controls whether to include the dot or not. Defaults to True.
    -
    -    Returns:
    -        str: extension
    -    """
    -    splitted = os.path.splitext(path)
    -    ext = ""
    -    if len(splitted) == 1:
    -        return ext
    -
    -    if include_dot:
    -        return splitted[1]
    -    else:
    -        return splitted[1].strip(".")
    -
    -
    -
    -def extension(path, include_dot=True) -
    -
    -

    Gets the extension of path -'/home/ahmed/myfile.py' -> .py if include_dot else py

    -

    Args

    -
    -
    path : str
    -
    [description]
    -
    include_dot : bool, optional
    -
    controls whether to include the dot or not. Defaults to True.
    -
    -

    Returns

    -
    -
    str
    -
    extension
    -
    -
    -Source code -
    def extension(path: str, include_dot=True):
    -    """Gets the extension of path
    -    '/home/ahmed/myfile.py' -> `.py` if include_dot else `py`
    -
    -    Args:
    -        path (str): [description]
    -        include_dot (bool, optional): controls whether to include the dot or not. Defaults to True.
    -
    -    Returns:
    -        str: extension
    -    """
    -    splitted = os.path.splitext(path)
    -    ext = ""
    -    if len(splitted) == 1:
    -        return ext
    -
    -    if include_dot:
    -        return splitted[1]
    -    else:
    -        return splitted[1].strip(".")
    -
    -
    -
    -def get_temp_dirname(suffix=None, prefix=None, dir=None) -
    -
    -

    Get temp directory name

    -

    Args

    -
    -
    suffix : [type], optional
    -
    ending suffix. Defaults to None.
    -
    prefix : [type], optional
    -
    prefix . Defaults to None.
    -
    dir : [type], optional
    -
    where to create the directory. Defaults to None.
    -
    -

    Returns

    -
    -
    str
    -
    temp directory name.
    -
    -
    -Source code -
    def get_temp_dirname(suffix=None, prefix=None, dir=None) -> str:
    -    """Get temp directory name
    -
    -    Args:
    -        suffix ([type], optional): ending suffix. Defaults to None.
    -        prefix ([type], optional): prefix . Defaults to None.
    -        dir ([type], optional): where to create the directory. Defaults to None.
    -
    -
    -    Returns:
    -        str: temp directory name.
    -    """
    -    return tempfile.TemporaryDirectory(suffix, prefix, dir).name
    -
    -
    -
    -def get_temp_filename(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None) -
    -
    -

    Get temp filename

    -

    Args

    -
    -
    mode : str, optional
    -
    [description]. Defaults to "w+b".
    -
    buffering : int, optional
    -
    buffering. Defaults to -1.
    -
    encoding : [type], optional
    -
    encoding . Defaults to None.
    -
    newline : [type], optional
    -
    Defaults to None.
    -
    suffix : [type], optional
    -
    ending suffix. Defaults to None.
    -
    prefix : [type], optional
    -
    prefix . Defaults to None.
    -
    dir : [type], optional
    -
    where to create the file. Defaults to None.
    -
    -

    Returns

    -

    [str]: temp filename

    -
    -Source code -
    def get_temp_filename(mode="w+b", buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None) -> str:
    -    """Get temp filename
    -
    -    Args:
    -        mode (str, optional): [description]. Defaults to "w+b".
    -        buffering (int, optional): buffering. Defaults to -1.
    -        encoding ([type], optional): encoding . Defaults to None.
    -        newline ([type], optional):  Defaults to None.
    -        suffix ([type], optional): ending suffix. Defaults to None.
    -        prefix ([type], optional): prefix . Defaults to None.
    -        dir ([type], optional): where to create the file. Defaults to None.
    -
    -    Returns:
    -        [str]: temp filename
    -    """
    -    return tempfile.NamedTemporaryFile(mode, buffering, encoding, newline, suffix, prefix, dir).name
    -
    -
    -
    -def home() -
    -
    -
    -
    -Source code -
    def home():
    -    return str(pathlib.Path.home())
    -
    -
    -
    -def is_absolute(path) -
    -
    -

    Checks if path is absolute

    -

    Returns

    -
    -
    bool
    -
    True if absolute
    -
    -
    -Source code -
    def is_absolute(path: str) -> bool:
    -    """Checks if path is absolute
    -
    -    Returns:
    -        bool: True if absolute
    -    """
    -    return pathlib.Path(path).is_absolute()
    -
    -
    -
    -def is_ascii_file(path, checksize=4096) -
    -
    -

    Checks if file path is ascii

    -

    Args

    -
    -
    path : str
    -
    file path
    -
    checksize : int, optional
    -
    checksize. Defaults to 4096.
    -
    -

    Returns

    -
    -
    bool
    -
    True if ascii file
    -
    -
    -Source code -
    def is_ascii_file(path: str, checksize=4096) -> bool:
    -    """Checks if file `path` is ascii
    -
    -    Args:
    -        path (str): file path
    -        checksize (int, optional): checksize. Defaults to 4096.
    -
    -    Returns:
    -        bool: True if ascii file
    -    """
    -    # TODO: let's talk about checksize feature.
    -    try:
    -        with open(path, encoding="ascii") as f:
    -            f.read()
    -            return True
    -    except UnicodeDecodeError:
    -        return False
    -
    -
    -
    -def is_binary_file(path) -
    -
    -
    -
    -Source code -
    is_binary_file = lambda path: not is_ascii_file(path)
    -
    -
    - -
    -

    Checks if path is a broken symlink

    -

    Args

    -
    -
    path : str
    -
    path to check
    -
    clean : bool, optional
    -
    remove symlink if broken. Defaults to False.
    -
    -

    Raises

    -
    -
    NotImplementedError
    -
    [description]
    -
    -

    Returns

    -
    -
    bool
    -
    True if path is a broken symlink
    -
    -
    -Source code -
    def is_broken_link(path: str, clean=False) -> bool:
    -    """Checks if path is a broken symlink
    -
    -    Args:
    -        path (str): path to check
    -        clean (bool, optional): remove symlink if broken. Defaults to False.
    -
    -    Raises:
    -        NotImplementedError: [description]
    -
    -    Returns:
    -        bool: True if path is a broken symlink
    -    """
    -    raise NotImplementedError()
    -
    -
    -
    -def is_dir(path) -
    -
    -

    Checks if path is a dir

    -

    :param path: path to check -:type path: str -:return: True if is dir else False -:rtype: bool

    -
    -Source code -
    def is_dir(path: str) -> bool:
    -    """Checks if path is a dir
    -
    -    :param path: path to check
    -    :type path: str
    -    :return: True if is dir else False
    -    :rtype: bool
    -    """
    -    return pathlib.Path(path).is_dir()
    -
    -
    -
    -def is_empty_dir(path) -
    -
    -

    Checks if path is emptry directory

    -

    Args

    -
    -
    path : str
    -
    path to check if empty directory
    -
    -

    Returns

    -
    -
    bool
    -
    True if path is emptry directory
    -
    -
    -Source code -
    def is_empty_dir(path: str) -> bool:
    -    """Checks if path is emptry directory
    -
    -    Args:
    -        path (str): path to check if empty directory
    -
    -    Returns:
    -        bool: True if path is emptry directory
    -    """
    -
    -    try:
    -        g = pathlib.Path(path).iterdir()
    -        next(g)
    -    except StopIteration:
    -        # means we can't get next entry -> dir is empty.
    -        return True
    -    else:
    -        return False
    -
    -
    -
    -def is_file(path) -
    -
    -

    Checks if path is a file

    -

    Args

    -
    -
    path : str
    -
    path to check if is file
    -
    -

    Returns

    -
    -
    bool
    -
    True if is file and False otherwise
    -
    -
    -Source code -
    def is_file(path: str) -> bool:
    -    """Checks if path is a file
    -
    -    Args:
    -        path (str): path to check if is file
    -
    -    Returns:
    -        bool: True if is file and False otherwise
    -    """
    -    return pathlib.Path(path).is_file()
    -
    -
    -
    -def is_mount(path) -
    -
    -

    Checks if path is mount

    -

    Returns

    -
    -
    bool
    -
    True if mount
    -
    -
    -Source code -
    def is_mount(path: str) -> bool:
    -    """Checks if path is mount
    -
    -    Returns:
    -        bool: True if mount
    -    """
    -    return pathlib.Path(path).is_mount()
    -
    -
    - -
    -

    Checks if path symlink

    -

    Args

    -
    -
    path : str
    -
    path to check if symlink
    -
    -

    Returns

    -
    -
    bool
    -
    True if symlink False otherwise
    -
    -
    -Source code -
    def is_symlink(path: str) -> bool:
    -    """Checks if path symlink
    -
    -    Args:
    -        path (str): path to check if symlink
    -
    -    Returns:
    -        bool: True if symlink False otherwise
    -    """
    -    return pathlib.Path(path).is_symlink()
    -
    -
    -
    -def join_paths(*paths) -
    -
    -
    -
    -Source code -
    def join_paths(*paths):
    -    return parts_to_path(paths)
    -
    -
    -
    -def lchmod(path, mode) -
    -
    -

    change file mode for path to mode (handles links too)

    -

    Args

    -
    -
    path : str
    -
    path
    -
    mode : int
    -
    file mode
    -
    -
    -Source code -
    def lchmod(path: str, mode):
    -    """change file mode for path to mode (handles links too)
    -
    -    Args:
    -        path (str): path
    -        mode (int): file mode
    -
    -    """
    -    return pathlib.Path(path).lchmod(mode)
    -
    -
    -
    -def lstat(path) -
    -
    -

    Gets stat of path path (handles links)

    -

    Args

    -
    -
    path : str
    -
    path to get its stat
    -
    -

    Returns

    -
    -
    stat_result
    -
    returns stat struct.
    -
    -
    -Source code -
    def lstat(path: str):
    -    """Gets stat of path `path` (handles links)
    -
    -    Args:
    -        path (str): path to get its stat
    -
    -    Returns:
    -        stat_result: returns stat struct.
    -    """
    -
    -    return pathlib.Path(path).lstat()
    -
    -
    -
    -def mkdir(path, exist_ok=True) -
    -
    -

    Makes directory at path

    -

    Args

    -
    -
    path : str
    -
    path to create dir at
    -
    exist_ok : bool, optional
    -
    won't fail if directory exists. Defaults to True.
    -
    -

    Returns

    -
    -Source code -
    def mkdir(path: str, exist_ok=True):
    -    """Makes directory at path
    -
    -    Args:
    -        path (str): path to create dir at
    -        exist_ok (bool, optional): won't fail if directory exists. Defaults to True.
    -
    -    Returns:
    -        [type]: [description]
    -    """
    -    return pathlib.Path(path).mkdir(exist_ok=exist_ok)
    -
    -
    -
    -def mkdirs(path, exist_ok=True) -
    -
    -

    Creates dir as well as all non exisitng parents in the path

    -

    Args

    -
    -
    path : str
    -
    path to create dir at
    -
    exist_ok : bool, optional
    -
    won't fail if directory exists. Defaults to True.
    -
    -
    -Source code -
    def mkdirs(path: str, exist_ok=True):
    -    """Creates dir as well as all non exisitng parents in the path
    -
    -    Args:
    -        path (str): path to create dir at
    -        exist_ok (bool, optional): won't fail if directory exists. Defaults to True.
    -    """
    -    return os.makedirs(path, exist_ok=exist_ok)
    -
    -
    -
    -def parent(path) -
    -
    -

    Get path's parent

    -

    Args

    -
    -
    path : str
    -
    path to get its parent
    -
    -

    Returns

    -
    -
    str
    -
    parent path.
    -
    -
    -Source code -
    def parent(path: str) -> str:
    -    """Get path's parent
    -
    -    Args:
    -        path (str): path to get its parent
    -
    -    Returns:
    -        str: parent path.
    -    """
    -    return str(pathlib.Path(path).parent)
    -
    -
    -
    -def parents(path) -
    -
    -

    Get parents list

    -

    e.g

    -
    >>> j.sals.fs.parents("/tmp/home/ahmed/myfile.py")
    -[PosixPath('/tmp/home/ahmed'),
    -
    -

    PosixPath('/tmp/home'), -PosixPath('/tmp'), -PosixPath('/')]

    -

    Args

    -
    -
    path : str
    -
    path to get its parents
    -
    -

    Returns

    -
    -
    List[str]: list of parents()
    -
     
    -
    -
    -Source code -
    def parents(path: str) -> List[str]:
    -    """Get parents list
    -
    -    e.g
    -    >>> j.sals.fs.parents("/tmp/home/ahmed/myfile.py")
    -    [PosixPath('/tmp/home/ahmed'),
    -    PosixPath('/tmp/home'),
    -    PosixPath('/tmp'),
    -    PosixPath('/')]
    -
    -    Args:
    -        path (str): path to get its parents
    -
    -    Returns:
    -        List[str]: list of parents
    -    """
    -
    -    return list([str(p) for p in pathlib.Path(path).parents])
    -
    -
    -
    -def parts_to_path(parts) -
    -
    -

    Convert list of path parts into a path string

    -

    Args

    -
    -
    parts : List[str]
    -
    path parts
    -
    -

    Returns

    -
    -
    str
    -
    joined path parts
    -
    -
    -Source code -
    def parts_to_path(parts: List[str]) -> str:
    -    """Convert list of path parts into a path string
    -
    -    Args:
    -        parts (List[str]): path parts
    -
    -    Returns:
    -        str: joined path parts
    -    """
    -    path = pathlib.Path(parts[0])
    -    for p in parts[1:]:
    -        path = path.joinpath(p)
    -    return str(path)
    -
    -
    -
    -def path_parts(path) -
    -
    -

    Convert path to a list of parts -e.g -'/tmp/tmp-5383p1GOmMOOwvfi.tpl' -> -('/', 'tmp', 'tmp-5383p1GOmMOOwvfi.tpl')

    -

    Args

    -
    -
    path : str
    -
    path to convert to parts
    -
    -

    Returns

    -

    List[str]: path parts.

    -
    -Source code -
    def path_parts(path: str) -> List[str]:
    -    """Convert path to a list of parts
    -    e.g
    -     '/tmp/tmp-5383p1GOmMOOwvfi.tpl' ->  ('/', 'tmp', 'tmp-5383p1GOmMOOwvfi.tpl')
    -    Args:
    -        path (str): path to convert to parts
    -
    -    Returns:
    -        List[str]: path parts.
    -    """
    -    return pathlib.Path(path).parts
    -
    -
    -
    -def read_ascii(path) -
    -
    -

    read ascii content at path

    -

    Args

    -
    -
    path : str
    -
    ascii file path
    -
    -

    Returns

    -
    -
    str
    -
    ascii content in path
    -
    -
    -Source code -
    def read_text(path: str) -> str:
    -    """read ascii content at `path`
    -
    -    Args:
    -        path (str): ascii file path
    -
    -    Returns:
    -        str: ascii content in path
    -    """
    -    return pathlib.Path(path).read_text()
    -
    -
    -
    -def read_binary(path) -
    -
    -

    read binary content at path

    -

    Args

    -
    -
    path : str
    -
    binary file path
    -
    -

    Returns

    -
    -
    bytes
    -
    binary content in path
    -
    -
    -Source code -
    def read_bytes(path: str) -> bytes:
    -    """read binary content at `path`
    -
    -    Args:
    -        path (str): binary file path
    -
    -    Returns:
    -        bytes: binary content in path
    -    """
    -    return pathlib.Path(path).read_bytes()
    -
    -
    -
    -def read_bytes(path) -
    -
    -

    read binary content at path

    -

    Args

    -
    -
    path : str
    -
    binary file path
    -
    -

    Returns

    -
    -
    bytes
    -
    binary content in path
    -
    -
    -Source code -
    def read_bytes(path: str) -> bytes:
    -    """read binary content at `path`
    -
    -    Args:
    -        path (str): binary file path
    -
    -    Returns:
    -        bytes: binary content in path
    -    """
    -    return pathlib.Path(path).read_bytes()
    -
    -
    -
    -def read_file(path) -
    -
    -

    read ascii content at path

    -

    Args

    -
    -
    path : str
    -
    ascii file path
    -
    -

    Returns

    -
    -
    str
    -
    ascii content in path
    -
    -
    -Source code -
    def read_text(path: str) -> str:
    -    """read ascii content at `path`
    -
    -    Args:
    -        path (str): ascii file path
    -
    -    Returns:
    -        str: ascii content in path
    -    """
    -    return pathlib.Path(path).read_text()
    -
    -
    -
    -def read_file_binary(path) -
    -
    -

    read binary content at path

    -

    Args

    -
    -
    path : str
    -
    binary file path
    -
    -

    Returns

    -
    -
    bytes
    -
    binary content in path
    -
    -
    -Source code -
    def read_bytes(path: str) -> bytes:
    -    """read binary content at `path`
    -
    -    Args:
    -        path (str): binary file path
    -
    -    Returns:
    -        bytes: binary content in path
    -    """
    -    return pathlib.Path(path).read_bytes()
    -
    -
    - -
    -
    -
    -Source code -
    def read_link(path):
    -    raise NotImplementedError()
    -
    -
    -
    -def read_text(path) -
    -
    -

    read ascii content at path

    -

    Args

    -
    -
    path : str
    -
    ascii file path
    -
    -

    Returns

    -
    -
    str
    -
    ascii content in path
    -
    -
    -Source code -
    def read_text(path: str) -> str:
    -    """read ascii content at `path`
    -
    -    Args:
    -        path (str): ascii file path
    -
    -    Returns:
    -        str: ascii content in path
    -    """
    -    return pathlib.Path(path).read_text()
    -
    -
    - -
    -
    -
    -Source code -
    def remove_links(path):
    -    raise NotImplementedError()
    -
    -
    -
    -def rename(path1, path2) -
    -
    -

    Rename path1 to path2

    -

    Args

    -
    -
    path1 : str
    -
    source path
    -
    path2 : str
    -
    dest path
    -
    -
    -Source code -
    def rename(path1: str, path2: str):
    -    """Rename path1 to path2
    -
    -    Args:
    -        path1 (str): source path
    -        path2 (str): dest path
    -
    -    """
    -    return pathlib.Path(path1).rename(path2)
    -
    -
    -
    -def replace_words_in_files(from_, to, where) -
    -
    -
    -
    -Source code -
    def replace_words_in_files(from_, to, where):
    -    pass
    -
    -
    -
    -def resolve(path) -
    -
    -

    resolve . and .. in path

    -

    Args

    -
    -
    path : str
    -
    path with optionally . and ..
    -
    -

    Returns

    -
    -
    str
    -
    resolved path
    -
    -
    -Source code -
    def resolve(path: str) -> str:
    -    """resolve `.` and `..` in path
    -
    -    Args:
    -        path (str): path with optionally `.` and `..`
    -
    -    Returns:
    -        str: resolved path
    -    """
    -    return pathlib.Path(path).resolve()
    -
    -
    -
    -def rm_emptry_dir(path) -
    -
    -

    Remove empty directory

    -

    Args

    -
    -
    path : str
    -
    path to remove.
    -
    -
    -Source code -
    def rm_emptry_dir(path: str):
    -    """Remove empty directory
    -
    -    Args:
    -        path (str): path to remove.
    -    """
    -    path = pathlib.Path(path)
    -    path.rmdir()
    -
    -
    -
    -def rmtree(path) -
    -
    -

    Remove directory tree

    -

    Args

    -
    -
    path : str
    -
    path to remove
    -
    -
    -Source code -
    def rmtree(path: str):
    -    """Remove directory tree
    -    Args:
    -        path (str): path to remove
    -    """
    -    path = pathlib.Path(path)
    -    if path.is_file() or path.is_symlink():
    -        os.remove(path)
    -    elif path.is_dir():
    -        shutil.rmtree(path)
    -
    -
    -
    -def stat(path) -
    -
    -

    Gets stat of path path

    -

    Args

    -
    -
    path : str
    -
    path to get its stat
    -
    -

    Returns

    -
    -
    stat_result
    -
    returns stat struct.
    -
    -
    -Source code -
    def stat(path: str):
    -    """Gets stat of path `path`
    -
    -    Args:
    -        path (str): path to get its stat
    -
    -    Returns:
    -        stat_result: returns stat struct.
    -    """
    -
    -    return pathlib.Path(path).stat()
    -
    -
    -
    -def stem(path) -
    -
    -

    returns the stem of a path (path without parent directory and without extension) -e.g -In [2]: t = j.sals.fs.stem("/tmp/tmp-5383p1GOmMOOwvfi.tpl")

    -
    In [3]: t
    -Out[3]: 'tmp-5383p1GOmMOOwvfi'
    -
    -

    Args

    -
    -
    path : str
    -
    path we want to get its stem
    -
    -

    Returns

    -
    -Source code -
    def stem(path: str) -> str:
    -    """returns the stem of a path (path without parent directory and without extension)
    -    e.g
    -        In [2]: t = j.sals.fs.stem("/tmp/tmp-5383p1GOmMOOwvfi.tpl")
    -
    -        In [3]: t
    -        Out[3]: 'tmp-5383p1GOmMOOwvfi'
    -
    -    Args:
    -        path (str): path we want to get its stem
    -
    -    Returns:
    -        [type]: [description]
    -    """
    -    return pathlib.Path(path).stem
    -
    -
    - -
    -

    Create a symbolic link.

    -

    Args

    -
    -
    src : str
    -
    Source of link
    -
    dst : str
    -
    Destination path of link
    -
    overwrite : bool, optional
    -
    If link exists will delete it. Defaults to False.
    -
    -
    -Source code -
    def symlink(src: str, dst: str, overwrite=False):
    -    """Create a symbolic link.
    -
    -    Args:
    -        src (str): Source of link
    -        dst (str): Destination path of link
    -        overwrite (bool, optional): If link exists will delete it. Defaults to False.
    -    """
    -    if overwrite and exists(dst):
    -        os.unlink(dst)
    -
    -    os.symlink(src, dst)
    -
    -
    -
    -def touch(path) -
    -
    -

    create file

    -

    Args

    -
    -
    path : str
    -
    path to create file
    -
    -
    -Source code -
    def touch(path: str):
    -    """create file
    -
    -    Args:
    -        path (str): path to create file
    -
    -    """
    -    return pathlib.Path(path).touch()
    -
    -
    - -
    -

    unlink path

    -

    Args

    -
    -
    path : str
    -
    path to unlink
    -
    -
    -Source code -
    def unlink(path: str):
    -    """unlink path
    -
    -    Args:
    -        path (str): path to unlink
    -
    -
    -    """
    -    return pathlib.Path(path).unlink()
    -
    -
    -
    -def walk(path, pat='*', filter_fun=<function default_filter_fun>) -
    -
    -

    walk recursively on path -e.g -for el in walk('/tmp', filter_fun=j.sals.fs.is_file) : .. -for el in walk('/tmp', filter_fun=j.sals.fs.is_dir) : .. -for el in walk('/tmp', filter_fun= lambda x: len(x)>4 and (j.sals.fs.is_file(x) or j.sals.fs.is_dir(x)) ) : ..

    -

    Args

    -
    -
    path : str
    -
    path to walk over
    -
    pat : str, optional
    -
    pattern to match against. Defaults to "*".
    -
    filter_fun : Function, optional
    -
    filtering function. Defaults to default_filter_fun which accepts anything.
    -
    -
    -Source code -
    def walk(path: str, pat="*", filter_fun=default_filter_fun):
    -    """walk recursively on path
    -    e.g
    -        for el in walk('/tmp', filter_fun=j.sals.fs.is_file) : ..
    -        for el in walk('/tmp', filter_fun=j.sals.fs.is_dir) : ..
    -        for el in walk('/tmp', filter_fun= lambda x: len(x)>4 and (j.sals.fs.is_file(x) or j.sals.fs.is_dir(x)) ) : ..
    -
    -
    -    Args:
    -        path (str): path to walk over
    -        pat (str, optional): pattern to match against. Defaults to "*".
    -        filter_fun (Function, optional): filtering function. Defaults to default_filter_fun which accepts anything.
    -    """
    -    p = pathlib.Path(path)
    -    for entry in p.rglob(pat):
    -        # use rglob instead of glob("**/*")
    -        if filter_fun(entry):
    -            yield str(entry)
    -
    -
    -
    -def walk_dirs(path, recursive=True) -
    -
    -

    walk over directories in path and applies function fun -e.g

    -
    for el in walk_dirs('/tmp') : ..
    -
    -

    Args

    -
    -
    path : str
    -
    path to walk over
    -
    recursive : bool, optional
    -
    recursive or not. Defaults to True.
    -
    -
    -Source code -
    def walk_dirs(path, recursive=True):
    -    """
    -        walk over directories in path and applies function `fun`
    -    e.g
    -
    -        for el in walk_dirs('/tmp') : ..
    -
    -
    -    Args:
    -        path (str): path to walk over
    -        recursive (bool, optional): recursive or not. Defaults to True.
    -
    -
    -    """
    -    if recursive:
    -        return walk(path, filter_fun=is_dir)
    -    else:
    -        return walk_non_recursive(path, filter_fun=is_dir)
    -
    -
    -
    -def walk_files(path, recursive=True) -
    -
    -

    walk over files in path and applies function fun -e.g

    -
    for el in walk_files('/tmp') : ..
    -
    -

    Args

    -
    -
    path : str
    -
    path to walk over
    -
    recursive : bool, optional
    -
    recursive or not. Defaults to True.
    -
    -
    -Source code -
    def walk_files(path: str, recursive=True):
    -    """
    -    walk over files in path and applies function `fun`
    -    e.g
    -
    -        for el in walk_files('/tmp') : ..
    -
    -    Args:
    -        path (str): path to walk over
    -        recursive (bool, optional): recursive or not. Defaults to True.
    -
    -
    -    """
    -
    -    if recursive:
    -        return walk(path, filter_fun=is_file)
    -    else:
    -        return walk_non_recursive(path, filter_fun=is_file)
    -
    -
    -
    -def walk_non_recursive(path, filter_fun=<function default_filter_fun>) -
    -
    -

    walks non recursively on path -e.g -for el in walk('/tmp', filter=j.sals.fs.is_file) : .. -for el in walk('/tmp', filter=j.sals.fs.is_dir) : .. -for el in walk('/tmp', filter= lambda x: len(x)>4 and (j.sals.fs.is_file(x) or j.sals.fs.is_dir(x)) ) : ..

    -

    Args

    -
    -
    path : str
    -
    path to walk over
    -
    pat : str, optional
    -
    pattern to match against. Defaults to "*".
    -
    filter_fun : Function, optional
    -
    filtering function. Defaults to default_filter_fun which accepts anything.
    -
    -
    -Source code -
    def walk_non_recursive(path: str, filter_fun=default_filter_fun):
    -    """walks non recursively on path
    -    e.g
    -        for el in walk('/tmp', filter=j.sals.fs.is_file) : ..
    -        for el in walk('/tmp', filter=j.sals.fs.is_dir) : ..
    -        for el in walk('/tmp', filter= lambda x: len(x)>4 and (j.sals.fs.is_file(x) or j.sals.fs.is_dir(x)) ) : ..
    -
    -
    -    Args:
    -        path (str): path to walk over
    -        pat (str, optional): pattern to match against. Defaults to "*".
    -        filter_fun (Function, optional): filtering function. Defaults to default_filter_fun which accepts anything.
    -    """
    -    p = pathlib.Path(path)
    -    for entry in p.iterdir():
    -        if filter_fun(entry):
    -            yield str(entry)
    -
    -
    -
    -def write_ascii(path, data, encoding=None) -
    -
    -

    write text data to path path with encoding

    -

    Args

    -
    -
    path : str
    -
    path to write to
    -
    data : str
    -
    ascii content
    -
    encoding : [type], optional
    -
    encoding. Defaults to None.
    -
    -
    -Source code -
    def write_text(path: str, data: str, encoding=None):
    -    """write text `data` to path `path` with encoding
    -
    -    Args:
    -        path (str): path to write to
    -        data (str): ascii content
    -        encoding ([type], optional): encoding. Defaults to None.
    -
    -
    -    """
    -    return pathlib.Path(path).write_text(data, encoding)
    -
    -
    -
    -def write_binary(path, data) -
    -
    -

    write binary data to path path

    -

    Args

    -
    -
    path : str
    -
    path to write to
    -
    data : bytes
    -
    binary content
    -
    -
    -Source code -
    def write_bytes(path: str, data: bytes):
    -    """write binary `data` to path `path`
    -
    -    Args:
    -        path (str): path to write to
    -        data (bytes): binary content
    -
    -    """
    -    return pathlib.Path(path).write_bytes(data)
    -
    -
    -
    -def write_bytes(path, data) -
    -
    -

    write binary data to path path

    -

    Args

    -
    -
    path : str
    -
    path to write to
    -
    data : bytes
    -
    binary content
    -
    -
    -Source code -
    def write_bytes(path: str, data: bytes):
    -    """write binary `data` to path `path`
    -
    -    Args:
    -        path (str): path to write to
    -        data (bytes): binary content
    -
    -    """
    -    return pathlib.Path(path).write_bytes(data)
    -
    -
    -
    -def write_file(path, data, encoding=None) -
    -
    -

    write text data to path path with encoding

    -

    Args

    -
    -
    path : str
    -
    path to write to
    -
    data : str
    -
    ascii content
    -
    encoding : [type], optional
    -
    encoding. Defaults to None.
    -
    -
    -Source code -
    def write_text(path: str, data: str, encoding=None):
    -    """write text `data` to path `path` with encoding
    -
    -    Args:
    -        path (str): path to write to
    -        data (str): ascii content
    -        encoding ([type], optional): encoding. Defaults to None.
    -
    -
    -    """
    -    return pathlib.Path(path).write_text(data, encoding)
    -
    -
    -
    -def write_file_binary(path, data) -
    -
    -

    write binary data to path path

    -

    Args

    -
    -
    path : str
    -
    path to write to
    -
    data : bytes
    -
    binary content
    -
    -
    -Source code -
    def write_bytes(path: str, data: bytes):
    -    """write binary `data` to path `path`
    -
    -    Args:
    -        path (str): path to write to
    -        data (bytes): binary content
    -
    -    """
    -    return pathlib.Path(path).write_bytes(data)
    -
    -
    -
    -def write_text(path, data, encoding=None) -
    -
    -

    write text data to path path with encoding

    -

    Args

    -
    -
    path : str
    -
    path to write to
    -
    data : str
    -
    ascii content
    -
    encoding : [type], optional
    -
    encoding. Defaults to None.
    -
    -
    -Source code -
    def write_text(path: str, data: str, encoding=None):
    -    """write text `data` to path `path` with encoding
    -
    -    Args:
    -        path (str): path to write to
    -        data (str): ascii content
    -        encoding ([type], optional): encoding. Defaults to None.
    -
    -
    -    """
    -    return pathlib.Path(path).write_text(data, encoding)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/sals/fs/fs2.html b/docs/api/jumpscale/sals/fs/fs2.html deleted file mode 100644 index dc307ae5c..000000000 --- a/docs/api/jumpscale/sals/fs/fs2.html +++ /dev/null @@ -1,1030 +0,0 @@ - - - - - - -jumpscale.sals.fs.fs2 API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.sals.fs.fs2

    -
    -
    -
    -Source code -
    import pathlib
    -import tempfile
    -import os
    -import shutil
    -import stat
    -from distutils import dir_util
    -
    -def home():
    -    return str(pathlib.Path.home())
    -
    -
    -def cwd():
    -    return str(pathlib.Path.cwd())
    -
    -
    -basename = os.path.basename
    -dirname = os.path.dirname
    -common_path = os.path.commonpath
    -common_prefix = os.path.commonprefix
    -norm_path = os.path.normpath
    -norm_case = os.path.normcase
    -get_access_time = os.path.getatime
    -get_modified_time = os.path.getmtime
    -get_creation_time = os.path.getctime
    -sep = os.path.sep
    -is_samefile = os.path.samefile
    -expandvars = os.path.expandvars
    -expanduser = os.path.expanduser
    -
    -def is_dir(path):
    -    return pathlib.Path(path).is_dir()
    -
    -def is_file(path):
    -    return pathlib.Path(path).is_file()
    -
    -
    -def is_symlink(path):
    -    return pathlib.Path(path).is_symlink()
    -
    -
    -def is_absolute(path):
    -    return pathlib.Path(path).is_absolute()
    -
    -
    -def is_mount(path):
    -    return pathlib.Path(path).is_mount()
    -
    -
    -def is_ascii_file(filename, checksize=4096):
    -    # TODO: let's talk about checksize feature.
    -    try:
    -        with open(filename, encoding="ascii") as f:
    -            f.read()
    -            return True
    -    except UnicodeDecodeError:
    -        return False
    -
    -
    -def is_empty_dir(path):
    -    try:
    -        g = pathlib.Path(path).iterdir()
    -        next(g)
    -    except StopIteration:
    -        # means we can't get next entry -> dir is empty.
    -        return True
    -    else:
    -        return False
    -
    -
    -is_binary_file = lambda path: not is_ascii_file(path)
    -
    -
    -def is_broken_link(path, clean=False):
    -    raise NotImplementedError()
    -
    -
    -def stem(path):
    -    return pathlib.Path(path).stem
    -
    -
    -def mkdir(path, exist_ok=True):
    -    return pathlib.Path(path).mkdir(exist_ok=exist_ok)
    -
    -
    -def parent(path):
    -    return pathlib.Path(path).parent
    -
    -
    -def parents(path):
    -    """
    -    [PosixPath('/tmp/home/ahmed'),
    -    PosixPath('/tmp/home'),
    -    PosixPath('/tmp'),
    -    PosixPath('/')]
    -    """
    -    return list(pathlib.Path(path).parnets)
    -
    -
    -def path_parts(path):
    -    return pathlib.Path(path).parts
    -
    -
    -def exists(path):
    -    return pathlib.Path(path).exists()
    -
    -
    -def rename(path1, path2):
    -    return pathlib.Path(path1).rename(path2)
    -
    -
    -def expanduser(path):
    -    return pathlib.Path(path).expanduser()
    -
    -
    -def unlink(path):
    -    return pathlib.Path(path).unlink()
    -
    -
    -def read_text(path):
    -    return pathlib.Path(path).read_text()
    -
    -
    -read_ascii = read_file = read_text
    -
    -
    -def read_bytes(path):
    -    return pathlib.Path(path).read_bytes()
    -
    -
    -read_binary = read_file_binary = read_bytes
    -
    -
    -def write_text(path, data, encoding=None):
    -    return pathlib.Path(path).write_text(data, encoding)
    -
    -
    -write_ascii = write_file = write_text
    -
    -
    -def write_bytes(path, data):
    -    return pathlib.Path(path).write_bytes(data)
    -
    -write_binary = write_file_binary = write_bytes
    -
    -def touch(path):
    -    return pathlib.Path(path).touch()
    -
    -
    -def get_temp_filename(mode="w+b", buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None):
    -    return tempfile.NamedTemporaryFile(mode, buffering, encoding, newline, suffix, prefix, dir).name
    -
    -
    -def get_temp_dirname(suffix=None, prefix=None, dir=None):
    -    return tempfile.TemporaryDirectory(suffix, prefix, dir).name
    -
    -
    -NamedTemporaryFile = tempfile.NamedTemporaryFile
    -TempraryDirectory = tempfile.TemporaryDirectory
    -mkdtemp = tempfile.mkdtemp
    -mkstemp = tempfile.mkstemp
    -get_temp_dir = tempfile.gettempdir
    -
    -def parts_to_path(parts):
    -    path = pathlib.Path(parts[0])
    -    for p in parts[1:]:
    -        path.joinpath(p)
    -
    -def rm_emptry_dir(path):
    -    path = pathlib.Path(path)
    -    path.rmdir()
    -
    -
    -def rmtree(path):
    -    path = pathlib.Path(path)
    -    if path.is_file() or path.is_link():
    -        os.remove(path)
    -    elif path.is_dir():
    -        shutil.rmtree(path)
    -    if not parents:
    -        return
    -    p = path.parent
    -    while p:
    -        try:
    -            os.rmdir(p)
    -        except os.error:
    -            break
    -        p = p.parent
    -
    -
    -def copy_stat(src, dst, times=True, perms=True):
    -    st = os.stat(src)
    -    if hasattr(os, 'utime'):
    -        os.utime(dst, (st.st_atime, st.st_mtime))
    -    if hasattr(os, 'chmod'):
    -        m = stat.S_IMODE(st.st_mode)
    -        os.chmod(dst, m)
    -
    -def copy_file(src, dst, times=False, perms=False):
    -    """Copy the file, optionally copying the permission bits (mode) and
    -        last access/modify time. If the destination file exists, it will be
    -        replaced. Raises OSError if the destination is a directory. If the
    -        platform does not have the ability to set the permission or times,
    -        ignore it.
    -        This is shutil.copyfile plus bits of shutil.copymode and
    -        shutil.copystat's implementation.
    -        shutil.copy and shutil.copy2 are not supported but are easy to do.
    -    """
    -    shutil.copyfile(src, dst)
    -    if times or perms:
    -        copy_stat(src, dst, times, perms)
    -
    -def copy_stat(src, dst, times=True, perms=True):
    -    st = os.stat(src)
    -    if hasattr(os, 'utime'):
    -        os.utime(dst, (st.st_atime, st.st_mtime))
    -    if hasattr(os, 'chmod'):
    -        m = stat.S_IMODE(st.st_mode)
    -        os.chmod(dst, m)
    -
    -copy_tree = dir_util.copy_tree
    -chdir = os.chdir
    -
    -def change_dir(to):
    -    os.chdir(path)
    -    return cwd()
    -
    -
    -def chmod(path, mode):
    -    return pathlib.Path(path).chmod(mode)
    -
    -def lchmod(path, mode):
    -    return pathlib.Path(path).lchmod(mode)
    -
    -
    -def stat(path):
    -    return pathlib.Path(path).stat()
    -
    -
    -def lstat(path):
    -    return pathlib.Path(path).lstat()
    -
    -
    -def resolve(path):
    -    return pathlib.Path(path).resolve()
    -
    -
    -def extension(path, include_dot=True):
    -    splitted = os.path.splitext(path)
    -    ext = ""
    -    if len(splitted) == 1:
    -        return ext
    -
    -    if include_dot:
    -        return splitted[1]
    -    else:
    -        return splitted[1].strip(".")
    -
    -def chown():
    -    raise NotImplementedError()
    -
    -def read_link(path):
    -    raise NotImplementedError()
    -
    -def remove_links(path):
    -    raise NotImplementedError()
    -
    -
    -def change_filenames(from_, to, where):
    -    pass
    -
    -def replace_words_in_files(from_, to, where):
    -    pass
    -
    -move = shutil.move
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def change_dir(to) -
    -
    -
    -
    -Source code -
    def change_dir(to):
    -    os.chdir(path)
    -    return cwd()
    -
    -
    -
    -def change_filenames(from_, to, where) -
    -
    -
    -
    -Source code -
    def change_filenames(from_, to, where):
    -    pass
    -
    -
    -
    -def chmod(path, mode) -
    -
    -
    -
    -Source code -
    def chmod(path, mode):
    -    return pathlib.Path(path).chmod(mode)
    -
    -
    -
    -def chown() -
    -
    -
    -
    -Source code -
    def chown():
    -    raise NotImplementedError()
    -
    -
    -
    -def copy_file(src, dst, times=False, perms=False) -
    -
    -

    Copy the file, optionally copying the permission bits (mode) and -last access/modify time. If the destination file exists, it will be -replaced. Raises OSError if the destination is a directory. If the -platform does not have the ability to set the permission or times, -ignore it. -This is shutil.copyfile plus bits of shutil.copymode and -shutil.copystat's implementation. -shutil.copy and shutil.copy2 are not supported but are easy to do.

    -
    -Source code -
    def copy_file(src, dst, times=False, perms=False):
    -    """Copy the file, optionally copying the permission bits (mode) and
    -        last access/modify time. If the destination file exists, it will be
    -        replaced. Raises OSError if the destination is a directory. If the
    -        platform does not have the ability to set the permission or times,
    -        ignore it.
    -        This is shutil.copyfile plus bits of shutil.copymode and
    -        shutil.copystat's implementation.
    -        shutil.copy and shutil.copy2 are not supported but are easy to do.
    -    """
    -    shutil.copyfile(src, dst)
    -    if times or perms:
    -        copy_stat(src, dst, times, perms)
    -
    -
    -
    -def copy_stat(src, dst, times=True, perms=True) -
    -
    -
    -
    -Source code -
    def copy_stat(src, dst, times=True, perms=True):
    -    st = os.stat(src)
    -    if hasattr(os, 'utime'):
    -        os.utime(dst, (st.st_atime, st.st_mtime))
    -    if hasattr(os, 'chmod'):
    -        m = stat.S_IMODE(st.st_mode)
    -        os.chmod(dst, m)
    -
    -
    -
    -def cwd() -
    -
    -
    -
    -Source code -
    def cwd():
    -    return str(pathlib.Path.cwd())
    -
    -
    -
    -def exists(path) -
    -
    -
    -
    -Source code -
    def exists(path):
    -    return pathlib.Path(path).exists()
    -
    -
    -
    -def expanduser(path) -
    -
    -
    -
    -Source code -
    def expanduser(path):
    -    return pathlib.Path(path).expanduser()
    -
    -
    -
    -def extension(path, include_dot=True) -
    -
    -
    -
    -Source code -
    def extension(path, include_dot=True):
    -    splitted = os.path.splitext(path)
    -    ext = ""
    -    if len(splitted) == 1:
    -        return ext
    -
    -    if include_dot:
    -        return splitted[1]
    -    else:
    -        return splitted[1].strip(".")
    -
    -
    -
    -def get_temp_dirname(suffix=None, prefix=None, dir=None) -
    -
    -
    -
    -Source code -
    def get_temp_dirname(suffix=None, prefix=None, dir=None):
    -    return tempfile.TemporaryDirectory(suffix, prefix, dir).name
    -
    -
    -
    -def get_temp_filename(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None) -
    -
    -
    -
    -Source code -
    def get_temp_filename(mode="w+b", buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None):
    -    return tempfile.NamedTemporaryFile(mode, buffering, encoding, newline, suffix, prefix, dir).name
    -
    -
    -
    -def home() -
    -
    -
    -
    -Source code -
    def home():
    -    return str(pathlib.Path.home())
    -
    -
    -
    -def is_absolute(path) -
    -
    -
    -
    -Source code -
    def is_absolute(path):
    -    return pathlib.Path(path).is_absolute()
    -
    -
    -
    -def is_ascii_file(filename, checksize=4096) -
    -
    -
    -
    -Source code -
    def is_ascii_file(filename, checksize=4096):
    -    # TODO: let's talk about checksize feature.
    -    try:
    -        with open(filename, encoding="ascii") as f:
    -            f.read()
    -            return True
    -    except UnicodeDecodeError:
    -        return False
    -
    -
    -
    -def is_binary_file(path) -
    -
    -
    -
    -Source code -
    is_binary_file = lambda path: not is_ascii_file(path)
    -
    -
    - -
    -
    -
    -Source code -
    def is_broken_link(path, clean=False):
    -    raise NotImplementedError()
    -
    -
    -
    -def is_dir(path) -
    -
    -
    -
    -Source code -
    def is_dir(path):
    -    return pathlib.Path(path).is_dir()
    -
    -
    -
    -def is_empty_dir(path) -
    -
    -
    -
    -Source code -
    def is_empty_dir(path):
    -    try:
    -        g = pathlib.Path(path).iterdir()
    -        next(g)
    -    except StopIteration:
    -        # means we can't get next entry -> dir is empty.
    -        return True
    -    else:
    -        return False
    -
    -
    -
    -def is_file(path) -
    -
    -
    -
    -Source code -
    def is_file(path):
    -    return pathlib.Path(path).is_file()
    -
    -
    -
    -def is_mount(path) -
    -
    -
    -
    -Source code -
    def is_mount(path):
    -    return pathlib.Path(path).is_mount()
    -
    -
    - -
    -
    -
    -Source code -
    def is_symlink(path):
    -    return pathlib.Path(path).is_symlink()
    -
    -
    -
    -def lchmod(path, mode) -
    -
    -
    -
    -Source code -
    def lchmod(path, mode):
    -    return pathlib.Path(path).lchmod(mode)
    -
    -
    -
    -def lstat(path) -
    -
    -
    -
    -Source code -
    def lstat(path):
    -    return pathlib.Path(path).lstat()
    -
    -
    -
    -def mkdir(path, exist_ok=True) -
    -
    -
    -
    -Source code -
    def mkdir(path, exist_ok=True):
    -    return pathlib.Path(path).mkdir(exist_ok=exist_ok)
    -
    -
    -
    -def parent(path) -
    -
    -
    -
    -Source code -
    def parent(path):
    -    return pathlib.Path(path).parent
    -
    -
    -
    -def parents(path) -
    -
    -

    [PosixPath('/tmp/home/ahmed'), -PosixPath('/tmp/home'), -PosixPath('/tmp'), -PosixPath('/')]

    -
    -Source code -
    def parents(path):
    -    """
    -    [PosixPath('/tmp/home/ahmed'),
    -    PosixPath('/tmp/home'),
    -    PosixPath('/tmp'),
    -    PosixPath('/')]
    -    """
    -    return list(pathlib.Path(path).parnets)
    -
    -
    -
    -def parts_to_path(parts) -
    -
    -
    -
    -Source code -
    def parts_to_path(parts):
    -    path = pathlib.Path(parts[0])
    -    for p in parts[1:]:
    -        path.joinpath(p)
    -
    -
    -
    -def path_parts(path) -
    -
    -
    -
    -Source code -
    def path_parts(path):
    -    return pathlib.Path(path).parts
    -
    -
    -
    -def read_ascii(path) -
    -
    -
    -
    -Source code -
    def read_text(path):
    -    return pathlib.Path(path).read_text()
    -
    -
    -
    -def read_binary(path) -
    -
    -
    -
    -Source code -
    def read_bytes(path):
    -    return pathlib.Path(path).read_bytes()
    -
    -
    -
    -def read_bytes(path) -
    -
    -
    -
    -Source code -
    def read_bytes(path):
    -    return pathlib.Path(path).read_bytes()
    -
    -
    -
    -def read_file(path) -
    -
    -
    -
    -Source code -
    def read_text(path):
    -    return pathlib.Path(path).read_text()
    -
    -
    -
    -def read_file_binary(path) -
    -
    -
    -
    -Source code -
    def read_bytes(path):
    -    return pathlib.Path(path).read_bytes()
    -
    -
    - -
    -
    -
    -Source code -
    def read_link(path):
    -    raise NotImplementedError()
    -
    -
    -
    -def read_text(path) -
    -
    -
    -
    -Source code -
    def read_text(path):
    -    return pathlib.Path(path).read_text()
    -
    -
    - -
    -
    -
    -Source code -
    def remove_links(path):
    -    raise NotImplementedError()
    -
    -
    -
    -def rename(path1, path2) -
    -
    -
    -
    -Source code -
    def rename(path1, path2):
    -    return pathlib.Path(path1).rename(path2)
    -
    -
    -
    -def replace_words_in_files(from_, to, where) -
    -
    -
    -
    -Source code -
    def replace_words_in_files(from_, to, where):
    -    pass
    -
    -
    -
    -def resolve(path) -
    -
    -
    -
    -Source code -
    def resolve(path):
    -    return pathlib.Path(path).resolve()
    -
    -
    -
    -def rm_emptry_dir(path) -
    -
    -
    -
    -Source code -
    def rm_emptry_dir(path):
    -    path = pathlib.Path(path)
    -    path.rmdir()
    -
    -
    -
    -def rmtree(path) -
    -
    -
    -
    -Source code -
    def rmtree(path):
    -    path = pathlib.Path(path)
    -    if path.is_file() or path.is_link():
    -        os.remove(path)
    -    elif path.is_dir():
    -        shutil.rmtree(path)
    -    if not parents:
    -        return
    -    p = path.parent
    -    while p:
    -        try:
    -            os.rmdir(p)
    -        except os.error:
    -            break
    -        p = p.parent
    -
    -
    -
    -def stat(path) -
    -
    -
    -
    -Source code -
    def stat(path):
    -    return pathlib.Path(path).stat()
    -
    -
    -
    -def stem(path) -
    -
    -
    -
    -Source code -
    def stem(path):
    -    return pathlib.Path(path).stem
    -
    -
    -
    -def touch(path) -
    -
    -
    -
    -Source code -
    def touch(path):
    -    return pathlib.Path(path).touch()
    -
    -
    - -
    -
    -
    -Source code -
    def unlink(path):
    -    return pathlib.Path(path).unlink()
    -
    -
    -
    -def write_ascii(path, data, encoding=None) -
    -
    -
    -
    -Source code -
    def write_text(path, data, encoding=None):
    -    return pathlib.Path(path).write_text(data, encoding)
    -
    -
    -
    -def write_binary(path, data) -
    -
    -
    -
    -Source code -
    def write_bytes(path, data):
    -    return pathlib.Path(path).write_bytes(data)
    -
    -
    -
    -def write_bytes(path, data) -
    -
    -
    -
    -Source code -
    def write_bytes(path, data):
    -    return pathlib.Path(path).write_bytes(data)
    -
    -
    -
    -def write_file(path, data, encoding=None) -
    -
    -
    -
    -Source code -
    def write_text(path, data, encoding=None):
    -    return pathlib.Path(path).write_text(data, encoding)
    -
    -
    -
    -def write_file_binary(path, data) -
    -
    -
    -
    -Source code -
    def write_bytes(path, data):
    -    return pathlib.Path(path).write_bytes(data)
    -
    -
    -
    -def write_text(path, data, encoding=None) -
    -
    -
    -
    -Source code -
    def write_text(path, data, encoding=None):
    -    return pathlib.Path(path).write_text(data, encoding)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/sals/hostsfile/hostsfile.html b/docs/api/jumpscale/sals/hostsfile/hostsfile.html deleted file mode 100644 index f802177b8..000000000 --- a/docs/api/jumpscale/sals/hostsfile/hostsfile.html +++ /dev/null @@ -1,363 +0,0 @@ - - - - - - -jumpscale.sals.hostsfile.hostsfile API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.sals.hostsfile.hostsfile

    -
    -
    -
    -Source code -
    import re
    -
    -
    -class HostsFile:
    -    def __init__(self, hosts_file_path):
    -        self.path = hosts_file_path
    -        self.content = self._parse()
    -
    -    def _parse(self):
    -        with open(self.path, "r") as file:
    -            content_text = file.read()
    -        content_lines = content_text.splitlines()
    -        ip_lines = []
    -        regex = r"""\d+\.\d+\.\d+\.\d+"""
    -        for line in content_lines:
    -            if re.search(regex, line):
    -                ip_lines.append(line)
    -        content_dict = {}
    -        for line in ip_lines:
    -            key, value = line.split("\t")[0], line.split("\t")[1]
    -            content_dict[key] = value
    -        return content_dict
    -
    -    def write(self):
    -        """
    -        write the changes into the file.
    -        """
    -        content_text = ""
    -        for key, value in self.content.items():
    -            content_text += f"\n{key}\t{value}"
    -        with open(self.path, "w") as file:
    -            file.write(content_text)
    -
    -    def remove(self, ip):
    -        """
    -        remove the ip and its hostname from hosts file
    -        Args:
    -            ip (str) : the ip address
    -        """
    -        del self.content[ip]
    -
    -    def add(self, ip, domain):
    -        """
    -        add new entry to the hosts file
    -        Args:
    -            ip (str) : the ip address
    -            domain (str) : the host name
    -        """
    -        self.content[ip] = domain
    -
    -    def set_hostname(self, ip, domain):
    -        """
    -        update the hostname for ip
    -        Args:
    -            ip (str) : the ip address
    -            domain (str) : the host name           
    -        """
    -        self.content[ip] = domain
    -
    -    def exists(self, ip):
    -        """
    -        check for the existence of the ip in hosts file.
    -        Args:
    -            ip (str) : the ip address
    -        Return:
    -            boolen expression
    -        """
    -        return ip in self.content
    -
    -    def get_hostname(self, ip):
    -        """
    -        get the hostname for ip
    -        Args:
    -            ip (str) : the ip address
    -        Returns:
    -            the hostname for the ip address
    -        """
    -        return self.content[ip]
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class HostsFile -(hosts_file_path) -
    -
    -
    -
    -Source code -
    class HostsFile:
    -    def __init__(self, hosts_file_path):
    -        self.path = hosts_file_path
    -        self.content = self._parse()
    -
    -    def _parse(self):
    -        with open(self.path, "r") as file:
    -            content_text = file.read()
    -        content_lines = content_text.splitlines()
    -        ip_lines = []
    -        regex = r"""\d+\.\d+\.\d+\.\d+"""
    -        for line in content_lines:
    -            if re.search(regex, line):
    -                ip_lines.append(line)
    -        content_dict = {}
    -        for line in ip_lines:
    -            key, value = line.split("\t")[0], line.split("\t")[1]
    -            content_dict[key] = value
    -        return content_dict
    -
    -    def write(self):
    -        """
    -        write the changes into the file.
    -        """
    -        content_text = ""
    -        for key, value in self.content.items():
    -            content_text += f"\n{key}\t{value}"
    -        with open(self.path, "w") as file:
    -            file.write(content_text)
    -
    -    def remove(self, ip):
    -        """
    -        remove the ip and its hostname from hosts file
    -        Args:
    -            ip (str) : the ip address
    -        """
    -        del self.content[ip]
    -
    -    def add(self, ip, domain):
    -        """
    -        add new entry to the hosts file
    -        Args:
    -            ip (str) : the ip address
    -            domain (str) : the host name
    -        """
    -        self.content[ip] = domain
    -
    -    def set_hostname(self, ip, domain):
    -        """
    -        update the hostname for ip
    -        Args:
    -            ip (str) : the ip address
    -            domain (str) : the host name           
    -        """
    -        self.content[ip] = domain
    -
    -    def exists(self, ip):
    -        """
    -        check for the existence of the ip in hosts file.
    -        Args:
    -            ip (str) : the ip address
    -        Return:
    -            boolen expression
    -        """
    -        return ip in self.content
    -
    -    def get_hostname(self, ip):
    -        """
    -        get the hostname for ip
    -        Args:
    -            ip (str) : the ip address
    -        Returns:
    -            the hostname for the ip address
    -        """
    -        return self.content[ip]
    -
    -

    Methods

    -
    -
    -def add(self, ip, domain) -
    -
    -

    add new entry to the hosts file

    -

    Args

    -

    ip (str) : the ip address -domain (str) : the host name

    -
    -Source code -
    def add(self, ip, domain):
    -    """
    -    add new entry to the hosts file
    -    Args:
    -        ip (str) : the ip address
    -        domain (str) : the host name
    -    """
    -    self.content[ip] = domain
    -
    -
    -
    -def exists(self, ip) -
    -
    -

    check for the existence of the ip in hosts file.

    -

    Args

    -

    ip (str) : the ip address

    -

    Return

    -

    boolen expression

    -
    -Source code -
    def exists(self, ip):
    -    """
    -    check for the existence of the ip in hosts file.
    -    Args:
    -        ip (str) : the ip address
    -    Return:
    -        boolen expression
    -    """
    -    return ip in self.content
    -
    -
    -
    -def get_hostname(self, ip) -
    -
    -

    get the hostname for ip

    -

    Args

    -

    ip (str) : the ip address

    -

    Returns

    -
    -
    the hostname for the ip address
    -
     
    -
    -
    -Source code -
    def get_hostname(self, ip):
    -    """
    -    get the hostname for ip
    -    Args:
    -        ip (str) : the ip address
    -    Returns:
    -        the hostname for the ip address
    -    """
    -    return self.content[ip]
    -
    -
    -
    -def remove(self, ip) -
    -
    -

    remove the ip and its hostname from hosts file

    -

    Args

    -

    ip (str) : the ip address

    -
    -Source code -
    def remove(self, ip):
    -    """
    -    remove the ip and its hostname from hosts file
    -    Args:
    -        ip (str) : the ip address
    -    """
    -    del self.content[ip]
    -
    -
    -
    -def set_hostname(self, ip, domain) -
    -
    -

    update the hostname for ip

    -

    Args

    -

    ip (str) : the ip address -domain (str) : the host name

    -
    -Source code -
    def set_hostname(self, ip, domain):
    -    """
    -    update the hostname for ip
    -    Args:
    -        ip (str) : the ip address
    -        domain (str) : the host name           
    -    """
    -    self.content[ip] = domain
    -
    -
    -
    -def write(self) -
    -
    -

    write the changes into the file.

    -
    -Source code -
    def write(self):
    -    """
    -    write the changes into the file.
    -    """
    -    content_text = ""
    -    for key, value in self.content.items():
    -        content_text += f"\n{key}\t{value}"
    -    with open(self.path, "w") as file:
    -        file.write(content_text)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/sals/process/process.html b/docs/api/jumpscale/sals/process/process.html deleted file mode 100644 index 721e4327b..000000000 --- a/docs/api/jumpscale/sals/process/process.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - -jumpscale.sals.process.process API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.sals.process.process

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/baseactor.html b/docs/api/jumpscale/servers/gedis/baseactor.html deleted file mode 100644 index 7bce6c2a5..000000000 --- a/docs/api/jumpscale/servers/gedis/baseactor.html +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - -jumpscale.servers.gedis.baseactor API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.servers.gedis.baseactor

    -
    -
    -
    -Source code -
    import inspect
    -import sys
    -from functools import wraps
    -from jumpscale.loader import j
    -
    -
    -def actor_method(func):
    -    @wraps(func)
    -    def wrapper(*args, **kwargs):
    -        # verify args and kwargs types
    -        signature = inspect.signature(func)
    -        try:
    -            bound = signature.bind(*args, **kwargs)
    -        except TypeError as e:
    -            raise j.exceptions.Value(str(e))
    -
    -        for name, value in bound.arguments.items():
    -            annotation = signature.parameters[name].annotation
    -            if annotation not in (None, inspect._empty) and not isinstance(value, annotation):
    -                raise j.exceptions.Value(
    -                    f"parameter ({name}) supposed to be of type ({annotation.__name__}), but found ({type(value).__name__})"
    -                )
    -
    -        # call method
    -        result = func(*bound.args, **bound.kwargs)
    -        # verify result type
    -        return_type = signature.return_annotation
    -        if return_type is inspect._empty or return_type is None:
    -            return_type = type(None)
    -
    -        if not isinstance(result, return_type):
    -            raise j.exceptions.Value(f"method is supposed to return ({return_type}), but it returned ({type(result)})")
    -
    -        return result
    -
    -    return wrapper
    -
    -
    -class BaseActor:
    -    def __init__(self):
    -        self.path = None
    -
    -    @actor_method
    -    def info(self) -> dict:
    -        info = {}
    -        info["path"] = self.path
    -        info["methods"] = {}
    -
    -        methods = inspect.getmembers(self, predicate=inspect.ismethod)
    -        for name, attr in methods:
    -            if name.startswith("_"):
    -                continue
    -
    -            signature = inspect.signature(attr)
    -            info["methods"][name] = {}
    -            info["methods"][name]["args"] = []
    -            info["methods"][name]["doc"] = attr.__doc__ or ""
    -
    -            for parameter_name, parameter in signature.parameters.items():
    -                info["methods"][name]["args"].append((parameter_name, parameter.annotation.__name__))
    -
    -        return info
    -
    -    def __validate_actor__(self):
    -        def validate_annotation(annotation, annotated):
    -            if annotation is None or annotation is inspect._empty:
    -                return
    -
    -            if not (inspect.isclass(annotation) and annotation.__class__ == type):
    -                raise ValueError("annotation must be a class type")
    -
    -            if annotation not in (str, int, float, list, tuple, dict, bool):
    -                if annotation.__module__ == "builtins":
    -                    raise ValueError(f"unsupported type ({annotation.__name__})")
    -
    -                for method in ["to_dict", "from_dict"]:
    -                    if method not in dir(annotation):
    -                        raise ValueError(
    -                            f"type ({annotation.__name__}) which annotate {annotated} doesn't have {method} method"
    -                        )
    -
    -        result = {"valid": True, "errors": {}}
    -        methods = inspect.getmembers(self, predicate=inspect.ismethod)
    -        for method_name, method_callable in methods:
    -            if method_name.startswith("_"):
    -                continue
    -
    -            result["errors"][method_name] = []
    -            signature = inspect.signature(method_callable)
    -            try:
    -                validate_annotation(signature.return_annotation, "return")
    -            except ValueError as e:
    -                result["errors"][method_name].append(str(e))
    -
    -            for name, parameter in signature.parameters.items():
    -                try:
    -                    validate_annotation(parameter.annotation, f"parameter ({name})")
    -                except ValueError as e:
    -                    result["errors"][method_name].append(str(e))
    -
    -        if any(result["errors"].values()):
    -            result["valid"] = False
    -
    -        return result
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def actor_method(func) -
    -
    -
    -
    -Source code -
    def actor_method(func):
    -    @wraps(func)
    -    def wrapper(*args, **kwargs):
    -        # verify args and kwargs types
    -        signature = inspect.signature(func)
    -        try:
    -            bound = signature.bind(*args, **kwargs)
    -        except TypeError as e:
    -            raise j.exceptions.Value(str(e))
    -
    -        for name, value in bound.arguments.items():
    -            annotation = signature.parameters[name].annotation
    -            if annotation not in (None, inspect._empty) and not isinstance(value, annotation):
    -                raise j.exceptions.Value(
    -                    f"parameter ({name}) supposed to be of type ({annotation.__name__}), but found ({type(value).__name__})"
    -                )
    -
    -        # call method
    -        result = func(*bound.args, **bound.kwargs)
    -        # verify result type
    -        return_type = signature.return_annotation
    -        if return_type is inspect._empty or return_type is None:
    -            return_type = type(None)
    -
    -        if not isinstance(result, return_type):
    -            raise j.exceptions.Value(f"method is supposed to return ({return_type}), but it returned ({type(result)})")
    -
    -        return result
    -
    -    return wrapper
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class BaseActor -
    -
    -
    -
    -Source code -
    class BaseActor:
    -    def __init__(self):
    -        self.path = None
    -
    -    @actor_method
    -    def info(self) -> dict:
    -        info = {}
    -        info["path"] = self.path
    -        info["methods"] = {}
    -
    -        methods = inspect.getmembers(self, predicate=inspect.ismethod)
    -        for name, attr in methods:
    -            if name.startswith("_"):
    -                continue
    -
    -            signature = inspect.signature(attr)
    -            info["methods"][name] = {}
    -            info["methods"][name]["args"] = []
    -            info["methods"][name]["doc"] = attr.__doc__ or ""
    -
    -            for parameter_name, parameter in signature.parameters.items():
    -                info["methods"][name]["args"].append((parameter_name, parameter.annotation.__name__))
    -
    -        return info
    -
    -    def __validate_actor__(self):
    -        def validate_annotation(annotation, annotated):
    -            if annotation is None or annotation is inspect._empty:
    -                return
    -
    -            if not (inspect.isclass(annotation) and annotation.__class__ == type):
    -                raise ValueError("annotation must be a class type")
    -
    -            if annotation not in (str, int, float, list, tuple, dict, bool):
    -                if annotation.__module__ == "builtins":
    -                    raise ValueError(f"unsupported type ({annotation.__name__})")
    -
    -                for method in ["to_dict", "from_dict"]:
    -                    if method not in dir(annotation):
    -                        raise ValueError(
    -                            f"type ({annotation.__name__}) which annotate {annotated} doesn't have {method} method"
    -                        )
    -
    -        result = {"valid": True, "errors": {}}
    -        methods = inspect.getmembers(self, predicate=inspect.ismethod)
    -        for method_name, method_callable in methods:
    -            if method_name.startswith("_"):
    -                continue
    -
    -            result["errors"][method_name] = []
    -            signature = inspect.signature(method_callable)
    -            try:
    -                validate_annotation(signature.return_annotation, "return")
    -            except ValueError as e:
    -                result["errors"][method_name].append(str(e))
    -
    -            for name, parameter in signature.parameters.items():
    -                try:
    -                    validate_annotation(parameter.annotation, f"parameter ({name})")
    -                except ValueError as e:
    -                    result["errors"][method_name].append(str(e))
    -
    -        if any(result["errors"].values()):
    -            result["valid"] = False
    -
    -        return result
    -
    -

    Subclasses

    - -

    Methods

    -
    -
    -def info(self) -
    -
    -
    -
    -Source code -
    @actor_method
    -def info(self) -> dict:
    -    info = {}
    -    info["path"] = self.path
    -    info["methods"] = {}
    -
    -    methods = inspect.getmembers(self, predicate=inspect.ismethod)
    -    for name, attr in methods:
    -        if name.startswith("_"):
    -            continue
    -
    -        signature = inspect.signature(attr)
    -        info["methods"][name] = {}
    -        info["methods"][name]["args"] = []
    -        info["methods"][name]["doc"] = attr.__doc__ or ""
    -
    -        for parameter_name, parameter in signature.parameters.items():
    -            info["methods"][name]["args"].append((parameter_name, parameter.annotation.__name__))
    -
    -    return info
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/example_actor.html b/docs/api/jumpscale/servers/gedis/example_actor.html deleted file mode 100644 index efd9ebefc..000000000 --- a/docs/api/jumpscale/servers/gedis/example_actor.html +++ /dev/null @@ -1,475 +0,0 @@ - - - - - - -jumpscale.servers.gedis.example_actor API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.servers.gedis.example_actor

    -
    -
    -
    -Source code -
    from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    -from typing import Sequence
    -from jumpscale.loader import j
    -import inspect, sys
    -
    -
    -class TestObject:
    -    def __init__(self):
    -        self.attr = None
    -
    -    def to_dict(self):
    -        return self.__dict__
    -
    -    def from_dict(self, ddict):
    -        self.__dict__ = ddict
    -
    -
    -class Example(BaseActor):
    -    @actor_method
    -    def add_two_ints(self, x: int, y: int) -> int:
    -        """Adds two ints
    -
    -        Arguments:
    -            x {int} -- first int
    -            y {int} -- second int
    -
    -        Returns:
    -            int -- the sum of the two ints
    -        """
    -        return x + y
    -
    -    @actor_method
    -    def concate_two_strings(self, x: str, y: str) -> str:
    -        """Concate two strings
    -
    -        Arguments:
    -            x {str} -- first string
    -            y {str} -- second string
    -
    -        Returns:
    -            str -- the concate of the two strings
    -        """
    -        return x + y
    -
    -    @actor_method
    -    def modify_object(self, myobj: list, new_value: int) -> list:
    -        """Modify atrribute attr of the given object
    -
    -        Arguments:
    -            myobj {TestObject} -- the object to be modified
    -
    -        Returns:
    -            TestObject -- modified object
    -        """
    -        for i in range(len(myobj)):
    -            myobj[i].attr = new_value * (i + 1)
    -        return myobj
    -
    -
    -Actor = Example
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Actor -
    -
    -
    -
    -Source code -
    class Example(BaseActor):
    -    @actor_method
    -    def add_two_ints(self, x: int, y: int) -> int:
    -        """Adds two ints
    -
    -        Arguments:
    -            x {int} -- first int
    -            y {int} -- second int
    -
    -        Returns:
    -            int -- the sum of the two ints
    -        """
    -        return x + y
    -
    -    @actor_method
    -    def concate_two_strings(self, x: str, y: str) -> str:
    -        """Concate two strings
    -
    -        Arguments:
    -            x {str} -- first string
    -            y {str} -- second string
    -
    -        Returns:
    -            str -- the concate of the two strings
    -        """
    -        return x + y
    -
    -    @actor_method
    -    def modify_object(self, myobj: list, new_value: int) -> list:
    -        """Modify atrribute attr of the given object
    -
    -        Arguments:
    -            myobj {TestObject} -- the object to be modified
    -
    -        Returns:
    -            TestObject -- modified object
    -        """
    -        for i in range(len(myobj)):
    -            myobj[i].attr = new_value * (i + 1)
    -        return myobj
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def add_two_ints(self, x, y) -
    -
    -

    Adds two ints

    -

    Arguments

    -

    x {int} – first int -y {int} – second int

    -

    Returns

    -
    -
    intthe sum of the two ints
    -
     
    -
    -
    -Source code -
    @actor_method
    -def add_two_ints(self, x: int, y: int) -> int:
    -    """Adds two ints
    -
    -    Arguments:
    -        x {int} -- first int
    -        y {int} -- second int
    -
    -    Returns:
    -        int -- the sum of the two ints
    -    """
    -    return x + y
    -
    -
    -
    -def concate_two_strings(self, x, y) -
    -
    -

    Concate two strings

    -

    Arguments

    -

    x {str} – first string -y {str} – second string

    -

    Returns

    -
    -
    strthe concate of the two strings
    -
     
    -
    -
    -Source code -
    @actor_method
    -def concate_two_strings(self, x: str, y: str) -> str:
    -    """Concate two strings
    -
    -    Arguments:
    -        x {str} -- first string
    -        y {str} -- second string
    -
    -    Returns:
    -        str -- the concate of the two strings
    -    """
    -    return x + y
    -
    -
    -
    -def modify_object(self, myobj, new_value) -
    -
    -

    Modify atrribute attr of the given object

    -

    Arguments

    -

    myobj {TestObject} – the object to be modified

    -

    Returns

    -
    -
    TestObjectmodified object
    -
     
    -
    -
    -Source code -
    @actor_method
    -def modify_object(self, myobj: list, new_value: int) -> list:
    -    """Modify atrribute attr of the given object
    -
    -    Arguments:
    -        myobj {TestObject} -- the object to be modified
    -
    -    Returns:
    -        TestObject -- modified object
    -    """
    -    for i in range(len(myobj)):
    -        myobj[i].attr = new_value * (i + 1)
    -    return myobj
    -
    -
    -
    -
    -
    -class Example -
    -
    -
    -
    -Source code -
    class Example(BaseActor):
    -    @actor_method
    -    def add_two_ints(self, x: int, y: int) -> int:
    -        """Adds two ints
    -
    -        Arguments:
    -            x {int} -- first int
    -            y {int} -- second int
    -
    -        Returns:
    -            int -- the sum of the two ints
    -        """
    -        return x + y
    -
    -    @actor_method
    -    def concate_two_strings(self, x: str, y: str) -> str:
    -        """Concate two strings
    -
    -        Arguments:
    -            x {str} -- first string
    -            y {str} -- second string
    -
    -        Returns:
    -            str -- the concate of the two strings
    -        """
    -        return x + y
    -
    -    @actor_method
    -    def modify_object(self, myobj: list, new_value: int) -> list:
    -        """Modify atrribute attr of the given object
    -
    -        Arguments:
    -            myobj {TestObject} -- the object to be modified
    -
    -        Returns:
    -            TestObject -- modified object
    -        """
    -        for i in range(len(myobj)):
    -            myobj[i].attr = new_value * (i + 1)
    -        return myobj
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def add_two_ints(self, x, y) -
    -
    -

    Adds two ints

    -

    Arguments

    -

    x {int} – first int -y {int} – second int

    -

    Returns

    -
    -
    intthe sum of the two ints
    -
     
    -
    -
    -Source code -
    @actor_method
    -def add_two_ints(self, x: int, y: int) -> int:
    -    """Adds two ints
    -
    -    Arguments:
    -        x {int} -- first int
    -        y {int} -- second int
    -
    -    Returns:
    -        int -- the sum of the two ints
    -    """
    -    return x + y
    -
    -
    -
    -def concate_two_strings(self, x, y) -
    -
    -

    Concate two strings

    -

    Arguments

    -

    x {str} – first string -y {str} – second string

    -

    Returns

    -
    -
    strthe concate of the two strings
    -
     
    -
    -
    -Source code -
    @actor_method
    -def concate_two_strings(self, x: str, y: str) -> str:
    -    """Concate two strings
    -
    -    Arguments:
    -        x {str} -- first string
    -        y {str} -- second string
    -
    -    Returns:
    -        str -- the concate of the two strings
    -    """
    -    return x + y
    -
    -
    -
    -def modify_object(self, myobj, new_value) -
    -
    -

    Modify atrribute attr of the given object

    -

    Arguments

    -

    myobj {TestObject} – the object to be modified

    -

    Returns

    -
    -
    TestObjectmodified object
    -
     
    -
    -
    -Source code -
    @actor_method
    -def modify_object(self, myobj: list, new_value: int) -> list:
    -    """Modify atrribute attr of the given object
    -
    -    Arguments:
    -        myobj {TestObject} -- the object to be modified
    -
    -    Returns:
    -        TestObject -- modified object
    -    """
    -    for i in range(len(myobj)):
    -        myobj[i].attr = new_value * (i + 1)
    -    return myobj
    -
    -
    -
    -
    -
    -class TestObject -
    -
    -
    -
    -Source code -
    class TestObject:
    -    def __init__(self):
    -        self.attr = None
    -
    -    def to_dict(self):
    -        return self.__dict__
    -
    -    def from_dict(self, ddict):
    -        self.__dict__ = ddict
    -
    -

    Methods

    -
    -
    -def from_dict(self, ddict) -
    -
    -
    -
    -Source code -
    def from_dict(self, ddict):
    -    self.__dict__ = ddict
    -
    -
    -
    -def to_dict(self) -
    -
    -
    -
    -Source code -
    def to_dict(self):
    -    return self.__dict__
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/example_greeter.html b/docs/api/jumpscale/servers/gedis/example_greeter.html deleted file mode 100644 index 94c0cd506..000000000 --- a/docs/api/jumpscale/servers/gedis/example_greeter.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - -jumpscale.servers.gedis.example_greeter API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.servers.gedis.example_greeter

    -
    -
    -
    -Source code -
    from jumpscale.servers.gedis.baseactor import BaseActor
    -
    -
    -class Greeter(BaseActor):
    -    def hi(self):
    -        """returns hello world
    -        """
    -        return "hello world"
    -
    -    def ping(self):
    -        """
    -        
    -        """
    -        return "pong no?"
    -
    -    def add2(self, a, b):
    -        """Add two args
    -        
    -        """
    -        print("A {} B {} ".format(a, b))
    -        return a + b
    -
    -
    -Actor = Greeter
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Actor -(*args, **kwargs) -
    -
    -
    -
    -Source code -
    class Greeter(BaseActor):
    -    def hi(self):
    -        """returns hello world
    -        """
    -        return "hello world"
    -
    -    def ping(self):
    -        """
    -        
    -        """
    -        return "pong no?"
    -
    -    def add2(self, a, b):
    -        """Add two args
    -        
    -        """
    -        print("A {} B {} ".format(a, b))
    -        return a + b
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def add2(self, a, b) -
    -
    -

    Add two args

    -
    -Source code -
    def add2(self, a, b):
    -    """Add two args
    -    
    -    """
    -    print("A {} B {} ".format(a, b))
    -    return a + b
    -
    -
    -
    -def hi(self) -
    -
    -

    returns hello world

    -
    -Source code -
    def hi(self):
    -    """returns hello world
    -    """
    -    return "hello world"
    -
    -
    -
    -def ping(self) -
    -
    -
    -
    -Source code -
    def ping(self):
    -    """
    -    
    -    """
    -    return "pong no?"
    -
    -
    -
    -
    -
    -class Greeter -(*args, **kwargs) -
    -
    -
    -
    -Source code -
    class Greeter(BaseActor):
    -    def hi(self):
    -        """returns hello world
    -        """
    -        return "hello world"
    -
    -    def ping(self):
    -        """
    -        
    -        """
    -        return "pong no?"
    -
    -    def add2(self, a, b):
    -        """Add two args
    -        
    -        """
    -        print("A {} B {} ".format(a, b))
    -        return a + b
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def add2(self, a, b) -
    -
    -

    Add two args

    -
    -Source code -
    def add2(self, a, b):
    -    """Add two args
    -    
    -    """
    -    print("A {} B {} ".format(a, b))
    -    return a + b
    -
    -
    -
    -def hi(self) -
    -
    -

    returns hello world

    -
    -Source code -
    def hi(self):
    -    """returns hello world
    -    """
    -    return "hello world"
    -
    -
    -
    -def ping(self) -
    -
    -
    -
    -Source code -
    def ping(self):
    -    """
    -    
    -    """
    -    return "pong no?"
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/index.html b/docs/api/jumpscale/servers/gedis/index.html deleted file mode 100644 index e159684dd..000000000 --- a/docs/api/jumpscale/servers/gedis/index.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - -jumpscale.servers.gedis API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.servers.gedis

    -
    -
    -

    Gedis server package provides all code needed to have an RPC server using redis protocol for messaging

    -

    Create Gedis instance

    -
    t = j.servers.gedis.get("test")
    -
    -

    this will create Gedis instance with name test running on -default port 16000

    -
    t = j.servers.gedis.new("test",port=1500)
    -
    -

    This will create Gedis instance with name test running on port 1500

    -

    Start Gedis server

    -
    t.start()
    -
    -

    Stop Gedis server

    -
    t.stop()
    -
    -

    ~> -redis-cli -p 16000 greeter hi -actor greeter isn't loaded -~> -redis-cli -p 16000 system register_actor greeter /home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py -(integer) -1 -~> -redis-cli -p 16000 greeter hi -hello world -~> -redis-cli -p 16000 greeter add2 jo deboeck -"jodeboeck" -~> -fuser -k 16000/tcp

    -

    16000/tcp: -29331 -~> -redis-cli -p 16000 greeter hi -actor greeter isn't loaded -~> -redis-cli -p 16000 system register_actor greeter /home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py -(integer) -1 -~> -redis-cli -p 16000 greeter hi -hello world -~> -redis-cli -p 16000 greeter ping

    -

    pong no? -~> -redis-cli -p 16000 greeter add2 reem khamis -"reemkhamis" -```

    -
    -Source code -
    """Gedis server package provides all code needed to have an RPC server using redis protocol for messaging
    -
    -### Create Gedis instance
    -```
    -t = j.servers.gedis.get("test")
    -```
    -this will create Gedis instance with name test running on  default port 16000
    -```
    -t = j.servers.gedis.new("test",port=1500)
    -```
    -This will create Gedis instance with name test running on port 1500
    -### Start Gedis server
    -```
    -t.start()
    -```
    -### Stop Gedis server
    -```
    -t.stop()
    -```
    -
    -~>  redis-cli -p 16000 greeter hi
    -actor greeter isn't loaded
    - ~>  redis-cli -p 16000 system register_actor greeter /home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py
    -(integer) -1
    - ~>  redis-cli -p 16000 greeter hi
    - hello world
    - ~>  redis-cli -p 16000 greeter add2 jo deboeck
    - "jodeboeck"
    - ~>  fuser -k 16000/tcp
    -
    -16000/tcp:           29331
    - ~>  redis-cli -p 16000 greeter hi
    - actor greeter isn't loaded
    - ~>  redis-cli -p 16000 system register_actor greeter /home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py
    -(integer) -1
    - ~>  redis-cli -p 16000 greeter hi
    - hello world
    - ~>  redis-cli -p 16000 greeter ping
    -
    -pong no?
    - ~>  redis-cli -p 16000 greeter add2 reem khamis
    -"reemkhamis"
    -```
    -"""
    -from jumpscale.core.base import StoredFactory
    -
    -
    -def export_module_as():
    -    from .server import GedisServer
    -    from jumpscale.loader import j
    -
    -    j.logger.register("gedis")
    -    return StoredFactory(GedisServer)
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.servers.gedis.baseactor
    -
    -
    -
    -
    jumpscale.servers.gedis.example_actor
    -
    -
    -
    -
    jumpscale.servers.gedis.server
    -
    -
    -
    -
    jumpscale.servers.gedis.systemactor
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    from .server import GedisServer
    -    from jumpscale.loader import j
    -
    -    j.logger.register("gedis")
    -    return StoredFactory(GedisServer)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/server.html b/docs/api/jumpscale/servers/gedis/server.html deleted file mode 100644 index 463bc4e7e..000000000 --- a/docs/api/jumpscale/servers/gedis/server.html +++ /dev/null @@ -1,1068 +0,0 @@ - - - - - - -jumpscale.servers.gedis.server API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.servers.gedis.server

    -
    -
    -
    -Source code -
    import inspect
    -import json
    -import sys
    -import os
    -from redis import Redis
    -from enum import Enum
    -from functools import partial
    -from io import BytesIO
    -from signal import SIGKILL, SIGTERM
    -import json
    -import gevent
    -from gevent.pool import Pool
    -from gevent import time
    -from gevent.server import StreamServer
    -from jumpscale.core.base import Base, fields
    -from jumpscale.loader import j
    -from redis.connection import DefaultParser, Encoder
    -from redis.exceptions import ConnectionError
    -from .baseactor import BaseActor
    -from .systemactor import CoreActor, SystemActor
    -
    -
    -def serialize(obj):
    -    if not isinstance(obj, (str, int, float, list, tuple, dict, bool)):
    -        module = inspect.getmodule(obj).__file__[:-3]
    -        return dict(__serialized__=True, module=module, type=obj.__class__.__name__, data=obj.to_dict())
    -    return obj
    -
    -
    -def deserialize(obj):
    -    if isinstance(obj, dict) and obj.get("__serialized__"):
    -        module = sys.modules[obj["module"]]
    -        object_instance = getattr(module, obj["type"])()
    -        object_instance.from_dict(obj["data"])
    -        return object_instance
    -    return obj
    -
    -
    -class GedisErrorTypes(Enum):
    -    NOT_FOUND = 0
    -    BAD_REQUEST = 1
    -    ACTOR_ERROR = 3
    -    INTERNAL_SERVER_ERROR = 4
    -    PERMISSION_ERROR = 5
    -
    -
    -EXCEPTIONS_MAP = {
    -    j.exceptions.Value: GedisErrorTypes.BAD_REQUEST.value,
    -    j.exceptions.NotFound: GedisErrorTypes.NOT_FOUND.value,
    -    j.exceptions.Permission: GedisErrorTypes.PERMISSION_ERROR.value,
    -}
    -
    -
    -class RedisConnectionAdapter:
    -    def __init__(self, sock):
    -        self.socket = sock
    -        self._sock = sock
    -        self.socket_timeout = 600
    -        self.socket_connect_timeout = 600
    -        self.socket_keepalive = True
    -        self.retry_on_timeout = True
    -        self.socket_keepalive_options = {}
    -        self.encoder = Encoder("utf", "strict", False)
    -
    -
    -class ResponseEncoder:
    -    def __init__(self, socket):
    -        self.socket = socket
    -        self.buffer = BytesIO()
    -
    -    def encode(self, value):
    -        """Respond with data."""
    -        if value is None:
    -            self._write_buffer("$-1\r\n")
    -        elif isinstance(value, int):
    -            self._write_buffer(":{}\r\n".format(value))
    -        elif isinstance(value, bool):
    -            self._write_buffer(":{}\r\n".format(1 if value else 0))
    -        elif isinstance(value, str):
    -            if "\n" in value:
    -                self._bulk(value)
    -            else:
    -                self._write_buffer("+{}\r\n".format(value))
    -        elif isinstance(value, bytes):
    -            self._bulkbytes(value)
    -        elif isinstance(value, list):
    -            if value and value[0] == "*REDIS*":
    -                value = value[1:]
    -            self._array(value)
    -        elif hasattr(value, "__repr__"):
    -            self._bulk(value.__repr__())
    -        else:
    -            value = j.data.serializers.json.dumps(value, encoding="utf-8")
    -            self.encode(value)
    -
    -        self._send()
    -
    -    def status(self, msg="OK"):
    -        """Send a status."""
    -        self._write_buffer("+{}\r\n".format(msg))
    -        self._send()
    -
    -    def error(self, msg):
    -        """Send an error."""
    -        # print("###:%s" % msg)
    -        self._write_buffer("-ERR {}\r\n".format(msg))
    -        self._send()
    -
    -    def _bulk(self, value):
    -        """Send part of a multiline reply."""
    -        data = ["$", str(len(value)), "\r\n", value, "\r\n"]
    -        self._write_buffer("".join(data))
    -
    -    def _array(self, value):
    -        """send an array."""
    -        self._write_buffer("*{}\r\n".format(len(value)))
    -        for item in value:
    -            self.encode(item)
    -
    -    def _bulkbytes(self, value):
    -        data = [b"$", str(len(value)).encode(), b"\r\n", value, b"\r\n"]
    -        self._write_buffer(b"".join(data))
    -
    -    def _write_buffer(self, data):
    -        if isinstance(data, str):
    -            data = data.encode()
    -
    -        self.buffer.write(data)
    -
    -    def _send(self):
    -        self.socket.sendall(self.buffer.getvalue())
    -        self.buffer = BytesIO()  # seems faster then truncating
    -
    -
    -SERIALIZABLE_TYPES = (str, int, float, list, tuple, dict, bool)
    -RESERVED_ACTOR_NAMES = ("core", "system")
    -
    -
    -class GedisServer(Base):
    -    host = fields.String(default="127.0.0.1")
    -    port = fields.Integer(default=16000)
    -    enable_system_actor = fields.Boolean(default=True)
    -    run_async = fields.Boolean(default=True)
    -    _actors = fields.Typed(dict, default={})
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -        self._core_actor = CoreActor()
    -        self._system_actor = SystemActor()
    -        self._loaded_actors = {"core": self._core_actor}
    -
    -    @property
    -    def actors(self):
    -        """Lists saved actors
    -
    -        Returns:
    -            list -- List of saved actors
    -        """
    -        return self._actors
    -
    -    def actor_add(self, actor_name: str, actor_path: str):
    -        """Adds an actor to the server
    -
    -        Arguments:
    -            actor_name {str} -- Actor name
    -            actor_path {str} -- Actor absolute path
    -
    -        Raises:
    -            j.exceptions.Value: raises if actor name is matched one of the reserved actor names
    -            j.exceptions.Value: raises if actor name is not a valid identifier
    -        """
    -        if actor_name in RESERVED_ACTOR_NAMES:
    -            raise j.exceptions.Value("Invalid actor name")
    -
    -        if not actor_name.isidentifier():
    -            raise j.exceptions.Value(f"Actor name should be a valid identifier")
    -
    -        self._actors[actor_name] = actor_path
    -
    -    def actor_delete(self, actor_name: str):
    -        """Removes an actor from the server
    -
    -        Arguments:
    -            actor_name {str} -- Actor name
    -        """
    -        self._actors.pop(actor_name, None)
    -
    -    def start(self):
    -        """Starts the server
    -        """
    -        # handle signals
    -        for signal_type in (SIGTERM, SIGKILL):
    -            gevent.signal(signal_type, self.stop)
    -
    -        # register system actor if enabled
    -        if self.enable_system_actor:
    -            self._register_actor("system", self._system_actor)
    -
    -        self._core_actor.set_server(self)
    -        self._system_actor.set_server(self)
    -
    -        # register saved actors
    -        for actor_name, actor_path in self._actors.items():
    -            self._system_actor.register_actor(actor_name, actor_path)
    -
    -        # start the server
    -        self._server = StreamServer((self.host, self.port), self._on_connection, spawn=Pool())
    -        self._server.reuse_addr = True
    -        self._server.start()
    -
    -        j.logger.info(f"Gedis server is started at {self.host}:{self.port}...")
    -
    -    def stop(self):
    -        """Stops the server
    -        """
    -        j.logger.info("Shutting down...")
    -        self._server.stop()
    -
    -    def _register_actor(self, actor_name: str, actor_module: BaseActor):
    -        self._loaded_actors[actor_name] = actor_module
    -
    -    def _unregister_actor(self, actor_name: str):
    -        self._loaded_actors.pop(actor_name, None)
    -
    -    def _execute(self, method, args, kwargs):
    -        response = {}
    -        try:
    -            response["result"] = method(*args, **kwargs)
    -
    -        except Exception as e:
    -            j.logger.exception(f"error while executing {method}", exception=e)
    -
    -            response["error"] = str(e)
    -            response["error_type"] = EXCEPTIONS_MAP.get(e.__class__, GedisErrorTypes.ACTOR_ERROR.value)
    -
    -        return response
    -
    -    def _on_connection(self, socket, address):
    -        j.logger.info("New connection from {}", address)
    -        parser = DefaultParser(65536)
    -        connection = RedisConnectionAdapter(socket)
    -        try:
    -            encoder = ResponseEncoder(socket)
    -            parser.on_connect(connection)
    -
    -            while True:
    -                response = dict(success=True, result=None, error=None, error_type=None, is_async=False, task_id=None)
    -                try:
    -                    request = parser.read_response()
    -
    -                    if len(request) < 2:
    -                        response["error"] = "invalid request"
    -                        response["error_type"] = GedisErrorTypes.BAD_REQUEST.value
    -
    -                    else:
    -                        actor_name = request.pop(0).decode()
    -                        method_name = request.pop(0).decode()
    -                        actor_object = self._loaded_actors.get(actor_name)
    -
    -                        if not actor_object:
    -                            response["error"] = "actor not found"
    -                            response["error_type"] = GedisErrorTypes.NOT_FOUND.value
    -
    -                        elif not hasattr(actor_object, method_name):
    -                            response["error"] = "method not found"
    -                            response["error_type"] = GedisErrorTypes.NOT_FOUND.value
    -
    -                        else:
    -                            j.logger.info(
    -                                "Executing method {} from actor {} to client {}", method_name, actor_name, address
    -                            )
    -
    -                            if request:
    -                                args, kwargs = json.loads(request.pop(0), object_hook=deserialize)
    -                            else:
    -                                args, kwargs = (), {}
    -
    -                            method = getattr(actor_object, method_name)
    -                            result = self._execute(method, args, kwargs)
    -                            response.update(result)
    -
    -                except ConnectionError:
    -                    j.logger.info("Client {} closed the connection", address)
    -
    -                except Exception as exception:
    -                    j.logger.exception("internal error", exception=exception)
    -                    response["error"] = "internal server error"
    -                    response["error_type"] = GedisErrorTypes.INTERNAL_SERVER_ERROR.value
    -
    -                response["success"] = response["error"] is None
    -                encoder.encode(json.dumps(response, default=serialize))
    -
    -            parser.on_disconnect()
    -
    -        except BrokenPipeError:
    -            pass
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def deserialize(obj) -
    -
    -
    -
    -Source code -
    def deserialize(obj):
    -    if isinstance(obj, dict) and obj.get("__serialized__"):
    -        module = sys.modules[obj["module"]]
    -        object_instance = getattr(module, obj["type"])()
    -        object_instance.from_dict(obj["data"])
    -        return object_instance
    -    return obj
    -
    -
    -
    -def serialize(obj) -
    -
    -
    -
    -Source code -
    def serialize(obj):
    -    if not isinstance(obj, (str, int, float, list, tuple, dict, bool)):
    -        module = inspect.getmodule(obj).__file__[:-3]
    -        return dict(__serialized__=True, module=module, type=obj.__class__.__name__, data=obj.to_dict())
    -    return obj
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class GedisErrorTypes -(*args, **kwargs) -
    -
    -

    An enumeration.

    -
    -Source code -
    class GedisErrorTypes(Enum):
    -    NOT_FOUND = 0
    -    BAD_REQUEST = 1
    -    ACTOR_ERROR = 3
    -    INTERNAL_SERVER_ERROR = 4
    -    PERMISSION_ERROR = 5
    -
    -

    Ancestors

    -
      -
    • enum.Enum
    • -
    -

    Class variables

    -
    -
    var ACTOR_ERROR
    -
    -
    -
    -
    var BAD_REQUEST
    -
    -
    -
    -
    var INTERNAL_SERVER_ERROR
    -
    -
    -
    -
    var NOT_FOUND
    -
    -
    -
    -
    var PERMISSION_ERROR
    -
    -
    -
    -
    -
    -
    -class GedisServer -(*args, **kwargs) -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class GedisServer(Base):
    -    host = fields.String(default="127.0.0.1")
    -    port = fields.Integer(default=16000)
    -    enable_system_actor = fields.Boolean(default=True)
    -    run_async = fields.Boolean(default=True)
    -    _actors = fields.Typed(dict, default={})
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -        self._core_actor = CoreActor()
    -        self._system_actor = SystemActor()
    -        self._loaded_actors = {"core": self._core_actor}
    -
    -    @property
    -    def actors(self):
    -        """Lists saved actors
    -
    -        Returns:
    -            list -- List of saved actors
    -        """
    -        return self._actors
    -
    -    def actor_add(self, actor_name: str, actor_path: str):
    -        """Adds an actor to the server
    -
    -        Arguments:
    -            actor_name {str} -- Actor name
    -            actor_path {str} -- Actor absolute path
    -
    -        Raises:
    -            j.exceptions.Value: raises if actor name is matched one of the reserved actor names
    -            j.exceptions.Value: raises if actor name is not a valid identifier
    -        """
    -        if actor_name in RESERVED_ACTOR_NAMES:
    -            raise j.exceptions.Value("Invalid actor name")
    -
    -        if not actor_name.isidentifier():
    -            raise j.exceptions.Value(f"Actor name should be a valid identifier")
    -
    -        self._actors[actor_name] = actor_path
    -
    -    def actor_delete(self, actor_name: str):
    -        """Removes an actor from the server
    -
    -        Arguments:
    -            actor_name {str} -- Actor name
    -        """
    -        self._actors.pop(actor_name, None)
    -
    -    def start(self):
    -        """Starts the server
    -        """
    -        # handle signals
    -        for signal_type in (SIGTERM, SIGKILL):
    -            gevent.signal(signal_type, self.stop)
    -
    -        # register system actor if enabled
    -        if self.enable_system_actor:
    -            self._register_actor("system", self._system_actor)
    -
    -        self._core_actor.set_server(self)
    -        self._system_actor.set_server(self)
    -
    -        # register saved actors
    -        for actor_name, actor_path in self._actors.items():
    -            self._system_actor.register_actor(actor_name, actor_path)
    -
    -        # start the server
    -        self._server = StreamServer((self.host, self.port), self._on_connection, spawn=Pool())
    -        self._server.reuse_addr = True
    -        self._server.start()
    -
    -        j.logger.info(f"Gedis server is started at {self.host}:{self.port}...")
    -
    -    def stop(self):
    -        """Stops the server
    -        """
    -        j.logger.info("Shutting down...")
    -        self._server.stop()
    -
    -    def _register_actor(self, actor_name: str, actor_module: BaseActor):
    -        self._loaded_actors[actor_name] = actor_module
    -
    -    def _unregister_actor(self, actor_name: str):
    -        self._loaded_actors.pop(actor_name, None)
    -
    -    def _execute(self, method, args, kwargs):
    -        response = {}
    -        try:
    -            response["result"] = method(*args, **kwargs)
    -
    -        except Exception as e:
    -            j.logger.exception(f"error while executing {method}", exception=e)
    -
    -            response["error"] = str(e)
    -            response["error_type"] = EXCEPTIONS_MAP.get(e.__class__, GedisErrorTypes.ACTOR_ERROR.value)
    -
    -        return response
    -
    -    def _on_connection(self, socket, address):
    -        j.logger.info("New connection from {}", address)
    -        parser = DefaultParser(65536)
    -        connection = RedisConnectionAdapter(socket)
    -        try:
    -            encoder = ResponseEncoder(socket)
    -            parser.on_connect(connection)
    -
    -            while True:
    -                response = dict(success=True, result=None, error=None, error_type=None, is_async=False, task_id=None)
    -                try:
    -                    request = parser.read_response()
    -
    -                    if len(request) < 2:
    -                        response["error"] = "invalid request"
    -                        response["error_type"] = GedisErrorTypes.BAD_REQUEST.value
    -
    -                    else:
    -                        actor_name = request.pop(0).decode()
    -                        method_name = request.pop(0).decode()
    -                        actor_object = self._loaded_actors.get(actor_name)
    -
    -                        if not actor_object:
    -                            response["error"] = "actor not found"
    -                            response["error_type"] = GedisErrorTypes.NOT_FOUND.value
    -
    -                        elif not hasattr(actor_object, method_name):
    -                            response["error"] = "method not found"
    -                            response["error_type"] = GedisErrorTypes.NOT_FOUND.value
    -
    -                        else:
    -                            j.logger.info(
    -                                "Executing method {} from actor {} to client {}", method_name, actor_name, address
    -                            )
    -
    -                            if request:
    -                                args, kwargs = json.loads(request.pop(0), object_hook=deserialize)
    -                            else:
    -                                args, kwargs = (), {}
    -
    -                            method = getattr(actor_object, method_name)
    -                            result = self._execute(method, args, kwargs)
    -                            response.update(result)
    -
    -                except ConnectionError:
    -                    j.logger.info("Client {} closed the connection", address)
    -
    -                except Exception as exception:
    -                    j.logger.exception("internal error", exception=exception)
    -                    response["error"] = "internal server error"
    -                    response["error_type"] = GedisErrorTypes.INTERNAL_SERVER_ERROR.value
    -
    -                response["success"] = response["error"] is None
    -                encoder.encode(json.dumps(response, default=serialize))
    -
    -            parser.on_disconnect()
    -
    -        except BrokenPipeError:
    -            pass
    -
    -

    Ancestors

    -
      -
    • Base
    • -
    • types.SimpleNamespace
    • -
    -

    Instance variables

    -
    -
    var actors
    -
    -

    Lists saved actors

    -

    Returns

    -
    -
    listList of saved actors
    -
     
    -
    -
    -Source code -
    @property
    -def actors(self):
    -    """Lists saved actors
    -
    -    Returns:
    -        list -- List of saved actors
    -    """
    -    return self._actors
    -
    -
    -
    var enable_system_actor
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var host
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var port
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var run_async
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    -

    Methods

    -
    -
    -def actor_add(self, actor_name, actor_path) -
    -
    -

    Adds an actor to the server

    -

    Arguments

    -

    actor_name {str} – Actor name -actor_path {str} – Actor absolute path

    -

    Raises

    -
    -
    j.exceptions.Value: raises if actor name is matched one of the reserved actor names
    -
     
    -
    j.exceptions.Value: raises if actor name is not a valid identifier
    -
     
    -
    -
    -Source code -
    def actor_add(self, actor_name: str, actor_path: str):
    -    """Adds an actor to the server
    -
    -    Arguments:
    -        actor_name {str} -- Actor name
    -        actor_path {str} -- Actor absolute path
    -
    -    Raises:
    -        j.exceptions.Value: raises if actor name is matched one of the reserved actor names
    -        j.exceptions.Value: raises if actor name is not a valid identifier
    -    """
    -    if actor_name in RESERVED_ACTOR_NAMES:
    -        raise j.exceptions.Value("Invalid actor name")
    -
    -    if not actor_name.isidentifier():
    -        raise j.exceptions.Value(f"Actor name should be a valid identifier")
    -
    -    self._actors[actor_name] = actor_path
    -
    -
    -
    -def actor_delete(self, actor_name) -
    -
    -

    Removes an actor from the server

    -

    Arguments

    -

    actor_name {str} – Actor name

    -
    -Source code -
    def actor_delete(self, actor_name: str):
    -    """Removes an actor from the server
    -
    -    Arguments:
    -        actor_name {str} -- Actor name
    -    """
    -    self._actors.pop(actor_name, None)
    -
    -
    -
    -def start(self) -
    -
    -

    Starts the server

    -
    -Source code -
    def start(self):
    -    """Starts the server
    -    """
    -    # handle signals
    -    for signal_type in (SIGTERM, SIGKILL):
    -        gevent.signal(signal_type, self.stop)
    -
    -    # register system actor if enabled
    -    if self.enable_system_actor:
    -        self._register_actor("system", self._system_actor)
    -
    -    self._core_actor.set_server(self)
    -    self._system_actor.set_server(self)
    -
    -    # register saved actors
    -    for actor_name, actor_path in self._actors.items():
    -        self._system_actor.register_actor(actor_name, actor_path)
    -
    -    # start the server
    -    self._server = StreamServer((self.host, self.port), self._on_connection, spawn=Pool())
    -    self._server.reuse_addr = True
    -    self._server.start()
    -
    -    j.logger.info(f"Gedis server is started at {self.host}:{self.port}...")
    -
    -
    -
    -def stop(self) -
    -
    -

    Stops the server

    -
    -Source code -
    def stop(self):
    -    """Stops the server
    -    """
    -    j.logger.info("Shutting down...")
    -    self._server.stop()
    -
    -
    -
    -

    Inherited members

    - -
    -
    -class RedisConnectionAdapter -(sock) -
    -
    -
    -
    -Source code -
    class RedisConnectionAdapter:
    -    def __init__(self, sock):
    -        self.socket = sock
    -        self._sock = sock
    -        self.socket_timeout = 600
    -        self.socket_connect_timeout = 600
    -        self.socket_keepalive = True
    -        self.retry_on_timeout = True
    -        self.socket_keepalive_options = {}
    -        self.encoder = Encoder("utf", "strict", False)
    -
    -
    -
    -class ResponseEncoder -(socket) -
    -
    -
    -
    -Source code -
    class ResponseEncoder:
    -    def __init__(self, socket):
    -        self.socket = socket
    -        self.buffer = BytesIO()
    -
    -    def encode(self, value):
    -        """Respond with data."""
    -        if value is None:
    -            self._write_buffer("$-1\r\n")
    -        elif isinstance(value, int):
    -            self._write_buffer(":{}\r\n".format(value))
    -        elif isinstance(value, bool):
    -            self._write_buffer(":{}\r\n".format(1 if value else 0))
    -        elif isinstance(value, str):
    -            if "\n" in value:
    -                self._bulk(value)
    -            else:
    -                self._write_buffer("+{}\r\n".format(value))
    -        elif isinstance(value, bytes):
    -            self._bulkbytes(value)
    -        elif isinstance(value, list):
    -            if value and value[0] == "*REDIS*":
    -                value = value[1:]
    -            self._array(value)
    -        elif hasattr(value, "__repr__"):
    -            self._bulk(value.__repr__())
    -        else:
    -            value = j.data.serializers.json.dumps(value, encoding="utf-8")
    -            self.encode(value)
    -
    -        self._send()
    -
    -    def status(self, msg="OK"):
    -        """Send a status."""
    -        self._write_buffer("+{}\r\n".format(msg))
    -        self._send()
    -
    -    def error(self, msg):
    -        """Send an error."""
    -        # print("###:%s" % msg)
    -        self._write_buffer("-ERR {}\r\n".format(msg))
    -        self._send()
    -
    -    def _bulk(self, value):
    -        """Send part of a multiline reply."""
    -        data = ["$", str(len(value)), "\r\n", value, "\r\n"]
    -        self._write_buffer("".join(data))
    -
    -    def _array(self, value):
    -        """send an array."""
    -        self._write_buffer("*{}\r\n".format(len(value)))
    -        for item in value:
    -            self.encode(item)
    -
    -    def _bulkbytes(self, value):
    -        data = [b"$", str(len(value)).encode(), b"\r\n", value, b"\r\n"]
    -        self._write_buffer(b"".join(data))
    -
    -    def _write_buffer(self, data):
    -        if isinstance(data, str):
    -            data = data.encode()
    -
    -        self.buffer.write(data)
    -
    -    def _send(self):
    -        self.socket.sendall(self.buffer.getvalue())
    -        self.buffer = BytesIO()  # seems faster then truncating
    -
    -

    Methods

    -
    -
    -def encode(self, value) -
    -
    -

    Respond with data.

    -
    -Source code -
    def encode(self, value):
    -    """Respond with data."""
    -    if value is None:
    -        self._write_buffer("$-1\r\n")
    -    elif isinstance(value, int):
    -        self._write_buffer(":{}\r\n".format(value))
    -    elif isinstance(value, bool):
    -        self._write_buffer(":{}\r\n".format(1 if value else 0))
    -    elif isinstance(value, str):
    -        if "\n" in value:
    -            self._bulk(value)
    -        else:
    -            self._write_buffer("+{}\r\n".format(value))
    -    elif isinstance(value, bytes):
    -        self._bulkbytes(value)
    -    elif isinstance(value, list):
    -        if value and value[0] == "*REDIS*":
    -            value = value[1:]
    -        self._array(value)
    -    elif hasattr(value, "__repr__"):
    -        self._bulk(value.__repr__())
    -    else:
    -        value = j.data.serializers.json.dumps(value, encoding="utf-8")
    -        self.encode(value)
    -
    -    self._send()
    -
    -
    -
    -def error(self, msg) -
    -
    -

    Send an error.

    -
    -Source code -
    def error(self, msg):
    -    """Send an error."""
    -    # print("###:%s" % msg)
    -    self._write_buffer("-ERR {}\r\n".format(msg))
    -    self._send()
    -
    -
    -
    -def status(self, msg='OK') -
    -
    -

    Send a status.

    -
    -Source code -
    def status(self, msg="OK"):
    -    """Send a status."""
    -    self._write_buffer("+{}\r\n".format(msg))
    -    self._send()
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/systemactor.html b/docs/api/jumpscale/servers/gedis/systemactor.html deleted file mode 100644 index cc7ac84b0..000000000 --- a/docs/api/jumpscale/servers/gedis/systemactor.html +++ /dev/null @@ -1,382 +0,0 @@ - - - - - - -jumpscale.servers.gedis.systemactor API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.servers.gedis.systemactor

    -
    -
    -
    -Source code -
    import os
    -import sys
    -import json
    -import inspect
    -from jumpscale.loader import j
    -from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    -
    -
    -class CoreActor(BaseActor):
    -    def __init__(self):
    -        super().__init__()
    -        self._server = None
    -        self.path = __file__
    -
    -    def set_server(self, server):
    -        self._server = server
    -
    -    @actor_method
    -    def list_actors(self) -> list:
    -        """List available actors
    -
    -        Returns:
    -            list -- list of available actors
    -        """
    -        return list(self._server._loaded_actors.keys())
    -
    -
    -class SystemActor(BaseActor):
    -    def __init__(self):
    -        super().__init__()
    -        self._server = None
    -        self.path = __file__
    -
    -    def set_server(self, server):
    -        self._server = server
    -
    -    @actor_method
    -    def register_actor(self, actor_name: str, actor_path: str, force_reload: bool = False) -> bool:
    -        """
    -        Register new actor
    -
    -        Args:
    -            actor_name (str): actor name within gedis server.
    -            actor_path (str): actor path on gedis server machine.
    -            force_reload (bool, optional): reload the module if set. Defaults to False.
    -
    -        Raises:
    -            j.exceptions.Validation: in case the actor is not valid
    -
    -        Returns:
    -            bool: True if registered
    -        """
    -        module = j.tools.codeloader.load_python_module(actor_path, force_reload=force_reload)
    -        actor = module.Actor()
    -        actor.path = actor_path
    -        result = actor.__validate_actor__()
    -
    -        if not result["valid"]:
    -            raise j.exceptions.Validation(
    -                "Actor {} is not valid, check the following errors {}".format(actor_name, result["errors"])
    -            )
    -
    -        self._server._register_actor(actor_name, actor)
    -        return True
    -
    -    @actor_method
    -    def unregister_actor(self, actor_name: str) -> bool:
    -        """Register actor
    -
    -        Arguments:
    -            actor_name {str} -- actor name
    -
    -        Returns:
    -            bool -- True if actors is unregistered
    -        """
    -        self._server._unregister_actor(actor_name)
    -        return True
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class CoreActor -
    -
    -
    -
    -Source code -
    class CoreActor(BaseActor):
    -    def __init__(self):
    -        super().__init__()
    -        self._server = None
    -        self.path = __file__
    -
    -    def set_server(self, server):
    -        self._server = server
    -
    -    @actor_method
    -    def list_actors(self) -> list:
    -        """List available actors
    -
    -        Returns:
    -            list -- list of available actors
    -        """
    -        return list(self._server._loaded_actors.keys())
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def list_actors(self) -
    -
    -

    List available actors

    -

    Returns

    -
    -
    listlist of available actors
    -
     
    -
    -
    -Source code -
    @actor_method
    -def list_actors(self) -> list:
    -    """List available actors
    -
    -    Returns:
    -        list -- list of available actors
    -    """
    -    return list(self._server._loaded_actors.keys())
    -
    -
    -
    -def set_server(self, server) -
    -
    -
    -
    -Source code -
    def set_server(self, server):
    -    self._server = server
    -
    -
    -
    -
    -
    -class SystemActor -
    -
    -
    -
    -Source code -
    class SystemActor(BaseActor):
    -    def __init__(self):
    -        super().__init__()
    -        self._server = None
    -        self.path = __file__
    -
    -    def set_server(self, server):
    -        self._server = server
    -
    -    @actor_method
    -    def register_actor(self, actor_name: str, actor_path: str, force_reload: bool = False) -> bool:
    -        """
    -        Register new actor
    -
    -        Args:
    -            actor_name (str): actor name within gedis server.
    -            actor_path (str): actor path on gedis server machine.
    -            force_reload (bool, optional): reload the module if set. Defaults to False.
    -
    -        Raises:
    -            j.exceptions.Validation: in case the actor is not valid
    -
    -        Returns:
    -            bool: True if registered
    -        """
    -        module = j.tools.codeloader.load_python_module(actor_path, force_reload=force_reload)
    -        actor = module.Actor()
    -        actor.path = actor_path
    -        result = actor.__validate_actor__()
    -
    -        if not result["valid"]:
    -            raise j.exceptions.Validation(
    -                "Actor {} is not valid, check the following errors {}".format(actor_name, result["errors"])
    -            )
    -
    -        self._server._register_actor(actor_name, actor)
    -        return True
    -
    -    @actor_method
    -    def unregister_actor(self, actor_name: str) -> bool:
    -        """Register actor
    -
    -        Arguments:
    -            actor_name {str} -- actor name
    -
    -        Returns:
    -            bool -- True if actors is unregistered
    -        """
    -        self._server._unregister_actor(actor_name)
    -        return True
    -
    -

    Ancestors

    - -

    Methods

    -
    -
    -def register_actor(self, actor_name, actor_path, force_reload=False) -
    -
    -

    Register new actor

    -

    Args

    -
    -
    actor_name : str
    -
    actor name within gedis server.
    -
    actor_path : str
    -
    actor path on gedis server machine.
    -
    force_reload : bool, optional
    -
    reload the module if set. Defaults to False.
    -
    -

    Raises

    -
    -
    j.exceptions.Validation: in case the actor is not valid
    -
     
    -
    -

    Returns

    -
    -
    bool
    -
    True if registered
    -
    -
    -Source code -
    @actor_method
    -def register_actor(self, actor_name: str, actor_path: str, force_reload: bool = False) -> bool:
    -    """
    -    Register new actor
    -
    -    Args:
    -        actor_name (str): actor name within gedis server.
    -        actor_path (str): actor path on gedis server machine.
    -        force_reload (bool, optional): reload the module if set. Defaults to False.
    -
    -    Raises:
    -        j.exceptions.Validation: in case the actor is not valid
    -
    -    Returns:
    -        bool: True if registered
    -    """
    -    module = j.tools.codeloader.load_python_module(actor_path, force_reload=force_reload)
    -    actor = module.Actor()
    -    actor.path = actor_path
    -    result = actor.__validate_actor__()
    -
    -    if not result["valid"]:
    -        raise j.exceptions.Validation(
    -            "Actor {} is not valid, check the following errors {}".format(actor_name, result["errors"])
    -        )
    -
    -    self._server._register_actor(actor_name, actor)
    -    return True
    -
    -
    -
    -def set_server(self, server) -
    -
    -
    -
    -Source code -
    def set_server(self, server):
    -    self._server = server
    -
    -
    -
    -def unregister_actor(self, actor_name) -
    -
    -

    Register actor

    -

    Arguments

    -

    actor_name {str} – actor name

    -

    Returns

    -
    -
    boolTrue if actors is unregistered
    -
     
    -
    -
    -Source code -
    @actor_method
    -def unregister_actor(self, actor_name: str) -> bool:
    -    """Register actor
    -
    -    Arguments:
    -        actor_name {str} -- actor name
    -
    -    Returns:
    -        bool -- True if actors is unregistered
    -    """
    -    self._server._unregister_actor(actor_name)
    -    return True
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis_http/index.html b/docs/api/jumpscale/servers/gedis_http/index.html deleted file mode 100644 index fc9d499ff..000000000 --- a/docs/api/jumpscale/servers/gedis_http/index.html +++ /dev/null @@ -1,485 +0,0 @@ - - - - - - -jumpscale.servers.gedis_http API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.servers.gedis_http

    -
    -
    -
    -Source code -
    import json
    -from jumpscale.core.base import Base, fields
    -from jumpscale.loader import j
    -from gevent.pool import Pool
    -from bottle import Bottle, abort, request, response
    -from jumpscale.servers.gedis.server import GedisErrorTypes
    -from gevent.pywsgi import WSGIServer
    -from jumpscale.core.base import StoredFactory
    -
    -
    -class GedisHTTPServer(Base):
    -    host = fields.String(default="127.0.0.1")
    -    port = fields.Integer(default=8000)
    -    allow_cors = fields.Boolean(default=True)
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -        self._app = Bottle()
    -        self._client = None
    -        http_methods = ["GET", "POST"]
    -        if self.allow_cors:
    -            http_methods.extend(["OPTIONS", "PUT", "DELETE"])
    -        self._app.route("/<package>/<actor>/<method>", http_methods, self.enable_cors(self.handler, self.allow_cors))
    -
    -    @property
    -    def client(self):
    -        if self._client is None:
    -            self._client = j.clients.gedis.get(self.instance_name)
    -            self._client.disable_deserialization = True
    -        return self._client
    -
    -    def make_response(self, code, content):
    -        response.status = code
    -        response.content_type = "application/json"
    -        return json.dumps(content)
    -
    -    def enable_cors(self, fn, allow_cors=True):
    -        def _enable_cors(*args, **kwargs):
    -            # set CORS headers
    -            response.headers["Access-Control-Allow-Origin"] = "*"
    -            response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, OPTIONS, DELETE"
    -            response.headers[
    -                "Access-Control-Allow-Headers"
    -            ] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token"
    -
    -            if request.method != "OPTIONS":
    -                # actual request; reply with the actual response
    -                return fn(*args, **kwargs)
    -
    -        if allow_cors:
    -            return _enable_cors
    -        else:
    -            return fn
    -
    -    def handler(self, package, actor, method):
    -        actors = self.client.actors
    -
    -        actor = getattr(actors, f"{package}_{actor}", None)
    -        if not actor:
    -            return self.make_response(400, {"error": "actor not found"})
    -
    -        method = getattr(actor, method, None)
    -        if not method:
    -            return self.make_response(400, {"error": "method not found"})
    -
    -        kwargs = request.json or dict()
    -        response = method(**kwargs)
    -
    -        if not response.success:
    -            if response.error_type == GedisErrorTypes.NOT_FOUND:
    -                return self.make_response(404, {"error": response.error})
    -
    -            elif response.error_type == GedisErrorTypes.BAD_REQUEST:
    -                return self.make_response(400, {"error": response.error})
    -
    -            elif response.error_type == GedisErrorTypes.PERMISSION_ERROR:
    -                return self.make_response(403, {"error": response.error})
    -
    -            else:
    -                return self.make_response(500, {"error": response.error})
    -
    -        return self.make_response(200, response.result)
    -
    -    @property
    -    def gevent_server(self):
    -        return WSGIServer((self.host, self.port), self._app, spawn=Pool())
    -
    -
    -def export_module_as():
    -    return StoredFactory(GedisHTTPServer)
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def export_module_as() -
    -
    -
    -
    -Source code -
    def export_module_as():
    -    return StoredFactory(GedisHTTPServer)
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class GedisHTTPServer -(*args, **kwargs) -
    -
    -

    A simple attribute-based namespace.

    -

    SimpleNamespace(**kwargs)

    -

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    -

    any instance can have an optional name and a parent.

    -
    class Person(Base):
    -    name = fields.String()
    -    age = fields.Float()
    -
    -p = Person(name="ahmed", age="19")
    -print(p.name, p.age)
    -
    -

    Args

    -
    -
    parent_ : Base, optional
    -
    parent instance. Defaults to None.
    -
    instance_name_ : str, optional
    -
    instance name. Defaults to None.
    -
    **values
    -
    any given field values to initiate the instance with
    -
    -
    -Source code -
    class GedisHTTPServer(Base):
    -    host = fields.String(default="127.0.0.1")
    -    port = fields.Integer(default=8000)
    -    allow_cors = fields.Boolean(default=True)
    -
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    -        self._app = Bottle()
    -        self._client = None
    -        http_methods = ["GET", "POST"]
    -        if self.allow_cors:
    -            http_methods.extend(["OPTIONS", "PUT", "DELETE"])
    -        self._app.route("/<package>/<actor>/<method>", http_methods, self.enable_cors(self.handler, self.allow_cors))
    -
    -    @property
    -    def client(self):
    -        if self._client is None:
    -            self._client = j.clients.gedis.get(self.instance_name)
    -            self._client.disable_deserialization = True
    -        return self._client
    -
    -    def make_response(self, code, content):
    -        response.status = code
    -        response.content_type = "application/json"
    -        return json.dumps(content)
    -
    -    def enable_cors(self, fn, allow_cors=True):
    -        def _enable_cors(*args, **kwargs):
    -            # set CORS headers
    -            response.headers["Access-Control-Allow-Origin"] = "*"
    -            response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, OPTIONS, DELETE"
    -            response.headers[
    -                "Access-Control-Allow-Headers"
    -            ] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token"
    -
    -            if request.method != "OPTIONS":
    -                # actual request; reply with the actual response
    -                return fn(*args, **kwargs)
    -
    -        if allow_cors:
    -            return _enable_cors
    -        else:
    -            return fn
    -
    -    def handler(self, package, actor, method):
    -        actors = self.client.actors
    -
    -        actor = getattr(actors, f"{package}_{actor}", None)
    -        if not actor:
    -            return self.make_response(400, {"error": "actor not found"})
    -
    -        method = getattr(actor, method, None)
    -        if not method:
    -            return self.make_response(400, {"error": "method not found"})
    -
    -        kwargs = request.json or dict()
    -        response = method(**kwargs)
    -
    -        if not response.success:
    -            if response.error_type == GedisErrorTypes.NOT_FOUND:
    -                return self.make_response(404, {"error": response.error})
    -
    -            elif response.error_type == GedisErrorTypes.BAD_REQUEST:
    -                return self.make_response(400, {"error": response.error})
    -
    -            elif response.error_type == GedisErrorTypes.PERMISSION_ERROR:
    -                return self.make_response(403, {"error": response.error})
    -
    -            else:
    -                return self.make_response(500, {"error": response.error})
    -
    -        return self.make_response(200, response.result)
    -
    -    @property
    -    def gevent_server(self):
    -        return WSGIServer((self.host, self.port), self._app, spawn=Pool())
    -
    -

    Ancestors

    -
      -
    • Base
    • -
    • types.SimpleNamespace
    • -
    -

    Instance variables

    -
    -
    var allow_cors
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var client
    -
    -
    -
    -Source code -
    @property
    -def client(self):
    -    if self._client is None:
    -        self._client = j.clients.gedis.get(self.instance_name)
    -        self._client.disable_deserialization = True
    -    return self._client
    -
    -
    -
    var gevent_server
    -
    -
    -
    -Source code -
    @property
    -def gevent_server(self):
    -    return WSGIServer((self.host, self.port), self._app, spawn=Pool())
    -
    -
    -
    var host
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    var port
    -
    -

    getter method this property

    -

    will call _get_value, which would if the value is already defined -and will get the default value if not

    -

    Returns

    -
    -
    any
    -
    the field value
    -
    -
    -Source code -
    def getter(self):
    -    """
    -    getter method this property
    -
    -    will call `_get_value`, which would if the value is already defined
    -    and will get the default value if not
    -
    -    Returns:
    -        any: the field value
    -    """
    -    return self._get_value(name, field)
    -
    -
    -
    -

    Methods

    -
    -
    -def enable_cors(self, fn, allow_cors=True) -
    -
    -
    -
    -Source code -
    def enable_cors(self, fn, allow_cors=True):
    -    def _enable_cors(*args, **kwargs):
    -        # set CORS headers
    -        response.headers["Access-Control-Allow-Origin"] = "*"
    -        response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, OPTIONS, DELETE"
    -        response.headers[
    -            "Access-Control-Allow-Headers"
    -        ] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token"
    -
    -        if request.method != "OPTIONS":
    -            # actual request; reply with the actual response
    -            return fn(*args, **kwargs)
    -
    -    if allow_cors:
    -        return _enable_cors
    -    else:
    -        return fn
    -
    -
    -
    -def handler(self, package, actor, method) -
    -
    -
    -
    -Source code -
    def handler(self, package, actor, method):
    -    actors = self.client.actors
    -
    -    actor = getattr(actors, f"{package}_{actor}", None)
    -    if not actor:
    -        return self.make_response(400, {"error": "actor not found"})
    -
    -    method = getattr(actor, method, None)
    -    if not method:
    -        return self.make_response(400, {"error": "method not found"})
    -
    -    kwargs = request.json or dict()
    -    response = method(**kwargs)
    -
    -    if not response.success:
    -        if response.error_type == GedisErrorTypes.NOT_FOUND:
    -            return self.make_response(404, {"error": response.error})
    -
    -        elif response.error_type == GedisErrorTypes.BAD_REQUEST:
    -            return self.make_response(400, {"error": response.error})
    -
    -        elif response.error_type == GedisErrorTypes.PERMISSION_ERROR:
    -            return self.make_response(403, {"error": response.error})
    -
    -        else:
    -            return self.make_response(500, {"error": response.error})
    -
    -    return self.make_response(200, response.result)
    -
    -
    -
    -def make_response(self, code, content) -
    -
    -
    -
    -Source code -
    def make_response(self, code, content):
    -    response.status = code
    -    response.content_type = "application/json"
    -    return json.dumps(content)
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/threesdk/container.html b/docs/api/jumpscale/threesdk/container.html deleted file mode 100644 index 510eccd9a..000000000 --- a/docs/api/jumpscale/threesdk/container.html +++ /dev/null @@ -1,385 +0,0 @@ - - - - - - -jumpscale.threesdk.container API documentation - - - - - - - - - - - -
    -
    -
    -

    Module jumpscale.threesdk.container

    -
    -
    -
    - -Expand source code - -
    from jumpscale.clients.docker.docker import DockerClient
    -from jumpscale.core.dirs.dirs import Dirs
    -from jumpscale.core.exceptions import Value
    -from jumpscale.core.executors.local import execute
    -
    -docker_client = DockerClient()
    -
    -
    -class Container:
    -    """Container management
    -    """
    -
    -    @staticmethod
    -    def install(name, image, development: bool = False, volumes=None):
    -        """Creates a container
    -
    -        Args:
    -            name (str): name of the container
    -            image (str): container image.
    -            development (bool, optional): if true will mount codedir. Defaults to False.
    -            volumes (dict, optional): paths to be mounted
    -
    -        Raises:
    -            Value: Container with specified name already exists
    -        """
    -        if docker_client.exists(name):
    -            raise Value("Container with specified name already exists")
    -
    -        volumes = volumes or {}
    -        if development:
    -            volumes = {Dirs.CODEDIR: {"bind": "/sandbox/code", "mode": "rw"}}
    -
    -        print(f"Creating container {name}")
    -        return docker_client.run(name, image, entrypoint="/sbin/my_init", volumes=volumes, detach=True)
    -
    -    @staticmethod
    -    def start(name):
    -        """Starts an existing container
    -
    -        Args:
    -            name (str): name of the container
    -        """
    -        if not docker_client.exists(name):
    -            raise Value("Container with specified name doesn't exist")
    -        docker_client.start(name)
    -
    -    @staticmethod
    -    def exec(name, cmd):
    -        """Execute command in container
    -
    -        Args:
    -            name (str): name of the container
    -            cmd (str or list): command to execute
    -        """
    -        basecmd = ["docker", "exec", "-it", name]
    -        if isinstance(cmd, str):
    -            basecmd.append(cmd)
    -        else:
    -            basecmd += cmd
    -        execute(basecmd, pty=True)
    -
    -    @staticmethod
    -    def stop(name):
    -        """Stops an existing container
    -
    -        Args:
    -            name (str): name of the container
    -        """
    -        if not docker_client.exists(name):
    -            raise Value("Container with specified name doesn't exist")
    -        docker_client.stop(name)
    -
    -    @staticmethod
    -    def delete(name):
    -        Container.stop(name)
    -        docker_client.delete(name)
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Container -
    -
    -

    Container management

    -
    - -Expand source code - -
    class Container:
    -    """Container management
    -    """
    -
    -    @staticmethod
    -    def install(name, image, development: bool = False, volumes=None):
    -        """Creates a container
    -
    -        Args:
    -            name (str): name of the container
    -            image (str): container image.
    -            development (bool, optional): if true will mount codedir. Defaults to False.
    -            volumes (dict, optional): paths to be mounted
    -
    -        Raises:
    -            Value: Container with specified name already exists
    -        """
    -        if docker_client.exists(name):
    -            raise Value("Container with specified name already exists")
    -
    -        volumes = volumes or {}
    -        if development:
    -            volumes = {Dirs.CODEDIR: {"bind": "/sandbox/code", "mode": "rw"}}
    -
    -        print(f"Creating container {name}")
    -        return docker_client.run(name, image, entrypoint="/sbin/my_init", volumes=volumes, detach=True)
    -
    -    @staticmethod
    -    def start(name):
    -        """Starts an existing container
    -
    -        Args:
    -            name (str): name of the container
    -        """
    -        if not docker_client.exists(name):
    -            raise Value("Container with specified name doesn't exist")
    -        docker_client.start(name)
    -
    -    @staticmethod
    -    def exec(name, cmd):
    -        """Execute command in container
    -
    -        Args:
    -            name (str): name of the container
    -            cmd (str or list): command to execute
    -        """
    -        basecmd = ["docker", "exec", "-it", name]
    -        if isinstance(cmd, str):
    -            basecmd.append(cmd)
    -        else:
    -            basecmd += cmd
    -        execute(basecmd, pty=True)
    -
    -    @staticmethod
    -    def stop(name):
    -        """Stops an existing container
    -
    -        Args:
    -            name (str): name of the container
    -        """
    -        if not docker_client.exists(name):
    -            raise Value("Container with specified name doesn't exist")
    -        docker_client.stop(name)
    -
    -    @staticmethod
    -    def delete(name):
    -        Container.stop(name)
    -        docker_client.delete(name)
    -
    -

    Subclasses

    -
      -
    • jumpscale.threesdk.threebot.ThreeBot
    • -
    -

    Static methods

    -
    -
    -def delete(name) -
    -
    -
    -
    - -Expand source code - -
    @staticmethod
    -def delete(name):
    -    Container.stop(name)
    -    docker_client.delete(name)
    -
    -
    -
    -def exec(name, cmd) -
    -
    -

    Execute command in container

    -

    Args

    -
    -
    name : str
    -
    name of the container
    -
    cmd : str or list
    -
    command to execute
    -
    -
    - -Expand source code - -
    @staticmethod
    -def exec(name, cmd):
    -    """Execute command in container
    -
    -    Args:
    -        name (str): name of the container
    -        cmd (str or list): command to execute
    -    """
    -    basecmd = ["docker", "exec", "-it", name]
    -    if isinstance(cmd, str):
    -        basecmd.append(cmd)
    -    else:
    -        basecmd += cmd
    -    execute(basecmd, pty=True)
    -
    -
    -
    -def install(name, image, development: bool = False, volumes=None) -
    -
    -

    Creates a container

    -

    Args

    -
    -
    name : str
    -
    name of the container
    -
    image : str
    -
    container image.
    -
    development : bool, optional
    -
    if true will mount codedir. Defaults to False.
    -
    volumes : dict, optional
    -
    paths to be mounted
    -
    -

    Raises

    -
    -
    Value
    -
    Container with specified name already exists
    -
    -
    - -Expand source code - -
    @staticmethod
    -def install(name, image, development: bool = False, volumes=None):
    -    """Creates a container
    -
    -    Args:
    -        name (str): name of the container
    -        image (str): container image.
    -        development (bool, optional): if true will mount codedir. Defaults to False.
    -        volumes (dict, optional): paths to be mounted
    -
    -    Raises:
    -        Value: Container with specified name already exists
    -    """
    -    if docker_client.exists(name):
    -        raise Value("Container with specified name already exists")
    -
    -    volumes = volumes or {}
    -    if development:
    -        volumes = {Dirs.CODEDIR: {"bind": "/sandbox/code", "mode": "rw"}}
    -
    -    print(f"Creating container {name}")
    -    return docker_client.run(name, image, entrypoint="/sbin/my_init", volumes=volumes, detach=True)
    -
    -
    -
    -def start(name) -
    -
    -

    Starts an existing container

    -

    Args

    -
    -
    name : str
    -
    name of the container
    -
    -
    - -Expand source code - -
    @staticmethod
    -def start(name):
    -    """Starts an existing container
    -
    -    Args:
    -        name (str): name of the container
    -    """
    -    if not docker_client.exists(name):
    -        raise Value("Container with specified name doesn't exist")
    -    docker_client.start(name)
    -
    -
    -
    -def stop(name) -
    -
    -

    Stops an existing container

    -

    Args

    -
    -
    name : str
    -
    name of the container
    -
    -
    - -Expand source code - -
    @staticmethod
    -def stop(name):
    -    """Stops an existing container
    -
    -    Args:
    -        name (str): name of the container
    -    """
    -    if not docker_client.exists(name):
    -        raise Value("Container with specified name doesn't exist")
    -    docker_client.stop(name)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - diff --git a/docs/api/jumpscale/threesdk/identitymanager.html b/docs/api/jumpscale/threesdk/identitymanager.html deleted file mode 100644 index 8f60cf0d0..000000000 --- a/docs/api/jumpscale/threesdk/identitymanager.html +++ /dev/null @@ -1,531 +0,0 @@ - - - - - - -jumpscale.threesdk.identitymanager API documentation - - - - - - - - - - - -
    -
    -
    -

    Module jumpscale.threesdk.identitymanager

    -
    -
    -
    - -Expand source code - -
    import binascii
    -
    -import requests
    -from collections import namedtuple
    -
    -from jumpscale.data.encryption import mnemonic
    -from jumpscale.data.nacl.jsnacl import NACL
    -from jumpscale.data.serializers import base64
    -from jumpscale.core import exceptions
    -from jumpscale.tools.console import ask_choice, ask_string, printcolors
    -
    -NETWORKS = {"mainnet": "explorer.grid.tf", "testnet": "explorer.testnet.grid.tf", "devnet": "explorer.devnet.grid.tf"}
    -
    -RESTART_CHOICE = "Restart from the begining"
    -REENTER_CHOICE = "Re-Enter your value"
    -CHOICES = [RESTART_CHOICE, REENTER_CHOICE]
    -
    -IdentityInfo = namedtuple("IdentityInfo", ["identity", "email", "words", "explorer"])
    -
    -
    -class IdentityManager:
    -    def __init__(self, identity: str = "", email: str = None, words: str = None, explorer: str = None):
    -        self.identity = identity
    -        self.email = email
    -        self.words = words
    -        self.explorer = explorer
    -
    -    def reset(self):
    -        self.identity = ""
    -        self.email = ""
    -        self.words = ""
    -        self.explorer = ""
    -
    -    def _check_keys(self, user_explorer_key, user_app):
    -        if not user_app:
    -            return True
    -        pub_key_app = base64.decode(user_app["publicKey"])
    -        if binascii.unhexlify(user_explorer_key) != pub_key_app:
    -            return False
    -        return True
    -
    -    def _get_user(self):
    -        response = requests.get(f"https://login.threefold.me/api/users/{self.identity}")
    -        if response.status_code == 404:
    -            raise exceptions.Value(
    -                "\nThis identity does not exist in 3bot mobile app connect, Please create an idenity first using 3Bot Connect mobile Application\n"
    -            )
    -        userdata = response.json()
    -
    -        resp = requests.get("https://{}/explorer/users".format(self.explorer), params={"name": self.identity})
    -        if resp.status_code == 404 or resp.json() == []:
    -            # creating new user
    -            user = {}
    -            user["name"] = userdata["doublename"]
    -            user["pubkey"] = base64.decode(userdata["publicKey"]).hex()
    -            printcolors(
    -                f"\nWelcome {{CYAN}}{userdata['doublename']}{{WHITE}}. Creating a new record on {{CYAN}}{self.explorer}{{RESET}}.\n"
    -            )
    -            return user, userdata
    -        else:
    -            users = resp.json()
    -
    -            if not self._check_keys(users[0]["pubkey"], userdata):
    -                raise exceptions.Value(
    -                    f"\nYour 3bot on {self.explorer} seems to have been previously registered with a different public key.\n"
    -                    f"The identity of {self.identity} is mismatched with 3bot connect app"
    -                    "Please contact support.grid.tf to reset it.\n"
    -                    "Note: use the same email registered on the explorer to contact support otherwise we cannot reset the account.\n"
    -                )
    -
    -            if users:
    -                return (users[0], userdata)
    -            return None, userdata
    -
    -    def _check_email(self, email):
    -        resp = requests.get("https://{}/explorer/users".format(self.explorer), params={"email": email})
    -        users = resp.json()
    -        if users:
    -            if users[0]["name"] == self.identity:
    -                return True
    -            else:
    -                return False
    -        else:
    -            return True
    -
    -    def ask_identity(self, identity=None, explorer=None):
    -        def _fill_identity_args(identity, explorer):
    -            def fill_words():
    -                words = ask_string("Copy the phrase from your 3bot Connect app here: ")
    -                self.words = words
    -
    -            def fill_identity():
    -                identity = ask_string("what is your threebot name (identity)? ")
    -                if "." not in identity:
    -                    identity += ".3bot"
    -                self.identity = identity
    -
    -            if identity:
    -                if self.identity != identity and self.identity:
    -                    self.reset()
    -                self.identity = identity
    -
    -            if explorer:
    -                self.explorer = explorer
    -            elif not self.explorer:
    -                response = ask_choice(
    -                    "Which network would you like to register to? ", ["mainnet", "testnet", "devnet", "none"]
    -                )
    -                self.explorer = NETWORKS.get(response, None)
    -            if not self.explorer:
    -                return True
    -
    -            user, user_app = None, None
    -            while not user:
    -                fill_identity()
    -                try:
    -                    user, user_app = self._get_user()
    -                except exceptions.Value as e:
    -                    response = ask_choice(f"{e}What would you like to do? ", CHOICES)
    -                    if response == RESTART_CHOICE:
    -                        return False
    -
    -            while not self.email:
    -                self.email = ask_string("What is the email address associated with your identity? ")
    -                if self._check_email(self.email):
    -                    break
    -                else:
    -                    self.email = None
    -                    response = ask_choice(
    -                        "This email is currently associated with another identity. What would you like to do? ",
    -                        CHOICES,
    -                    )
    -                    if response == RESTART_CHOICE:
    -                        return False
    -
    -            print("Configured email for this identity is {}".format(self.email))
    -
    -            # time to do validation of words
    -            hexkey = None
    -            while True:
    -                if not self.words:
    -                    fill_words()
    -                try:
    -                    seed = mnemonic.mnemonic_to_key(self.words.strip())
    -                    hexkey = NACL(seed).get_verify_key_hex()
    -                    if (user and hexkey != user["pubkey"]) or not self._check_keys(hexkey, user_app):
    -                        raise Exception
    -                    else:
    -                        return True
    -                except Exception:
    -                    choice = ask_choice(
    -                        "\nSeems one or more more words entered is invalid.\nWhat would you like to do? ", CHOICES,
    -                    )
    -                    if choice == RESTART_CHOICE:
    -                        return False
    -                    fill_words()
    -
    -        while True:
    -            if _fill_identity_args(identity, explorer):
    -                identity_info = IdentityInfo(self.identity, self.email, self.words, self.explorer)
    -                return identity_info
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class IdentityInfo -(identity, email, words, explorer) -
    -
    -

    IdentityInfo(identity, email, words, explorer)

    -

    Ancestors

    -
      -
    • builtins.tuple
    • -
    -

    Instance variables

    -
    -
    var email
    -
    -

    Alias for field number 1

    -
    -
    var explorer
    -
    -

    Alias for field number 3

    -
    -
    var identity
    -
    -

    Alias for field number 0

    -
    -
    var words
    -
    -

    Alias for field number 2

    -
    -
    -
    -
    -class IdentityManager -(identity: str = '', email: str = None, words: str = None, explorer: str = None) -
    -
    -
    -
    - -Expand source code - -
    class IdentityManager:
    -    def __init__(self, identity: str = "", email: str = None, words: str = None, explorer: str = None):
    -        self.identity = identity
    -        self.email = email
    -        self.words = words
    -        self.explorer = explorer
    -
    -    def reset(self):
    -        self.identity = ""
    -        self.email = ""
    -        self.words = ""
    -        self.explorer = ""
    -
    -    def _check_keys(self, user_explorer_key, user_app):
    -        if not user_app:
    -            return True
    -        pub_key_app = base64.decode(user_app["publicKey"])
    -        if binascii.unhexlify(user_explorer_key) != pub_key_app:
    -            return False
    -        return True
    -
    -    def _get_user(self):
    -        response = requests.get(f"https://login.threefold.me/api/users/{self.identity}")
    -        if response.status_code == 404:
    -            raise exceptions.Value(
    -                "\nThis identity does not exist in 3bot mobile app connect, Please create an idenity first using 3Bot Connect mobile Application\n"
    -            )
    -        userdata = response.json()
    -
    -        resp = requests.get("https://{}/explorer/users".format(self.explorer), params={"name": self.identity})
    -        if resp.status_code == 404 or resp.json() == []:
    -            # creating new user
    -            user = {}
    -            user["name"] = userdata["doublename"]
    -            user["pubkey"] = base64.decode(userdata["publicKey"]).hex()
    -            printcolors(
    -                f"\nWelcome {{CYAN}}{userdata['doublename']}{{WHITE}}. Creating a new record on {{CYAN}}{self.explorer}{{RESET}}.\n"
    -            )
    -            return user, userdata
    -        else:
    -            users = resp.json()
    -
    -            if not self._check_keys(users[0]["pubkey"], userdata):
    -                raise exceptions.Value(
    -                    f"\nYour 3bot on {self.explorer} seems to have been previously registered with a different public key.\n"
    -                    f"The identity of {self.identity} is mismatched with 3bot connect app"
    -                    "Please contact support.grid.tf to reset it.\n"
    -                    "Note: use the same email registered on the explorer to contact support otherwise we cannot reset the account.\n"
    -                )
    -
    -            if users:
    -                return (users[0], userdata)
    -            return None, userdata
    -
    -    def _check_email(self, email):
    -        resp = requests.get("https://{}/explorer/users".format(self.explorer), params={"email": email})
    -        users = resp.json()
    -        if users:
    -            if users[0]["name"] == self.identity:
    -                return True
    -            else:
    -                return False
    -        else:
    -            return True
    -
    -    def ask_identity(self, identity=None, explorer=None):
    -        def _fill_identity_args(identity, explorer):
    -            def fill_words():
    -                words = ask_string("Copy the phrase from your 3bot Connect app here: ")
    -                self.words = words
    -
    -            def fill_identity():
    -                identity = ask_string("what is your threebot name (identity)? ")
    -                if "." not in identity:
    -                    identity += ".3bot"
    -                self.identity = identity
    -
    -            if identity:
    -                if self.identity != identity and self.identity:
    -                    self.reset()
    -                self.identity = identity
    -
    -            if explorer:
    -                self.explorer = explorer
    -            elif not self.explorer:
    -                response = ask_choice(
    -                    "Which network would you like to register to? ", ["mainnet", "testnet", "devnet", "none"]
    -                )
    -                self.explorer = NETWORKS.get(response, None)
    -            if not self.explorer:
    -                return True
    -
    -            user, user_app = None, None
    -            while not user:
    -                fill_identity()
    -                try:
    -                    user, user_app = self._get_user()
    -                except exceptions.Value as e:
    -                    response = ask_choice(f"{e}What would you like to do? ", CHOICES)
    -                    if response == RESTART_CHOICE:
    -                        return False
    -
    -            while not self.email:
    -                self.email = ask_string("What is the email address associated with your identity? ")
    -                if self._check_email(self.email):
    -                    break
    -                else:
    -                    self.email = None
    -                    response = ask_choice(
    -                        "This email is currently associated with another identity. What would you like to do? ",
    -                        CHOICES,
    -                    )
    -                    if response == RESTART_CHOICE:
    -                        return False
    -
    -            print("Configured email for this identity is {}".format(self.email))
    -
    -            # time to do validation of words
    -            hexkey = None
    -            while True:
    -                if not self.words:
    -                    fill_words()
    -                try:
    -                    seed = mnemonic.mnemonic_to_key(self.words.strip())
    -                    hexkey = NACL(seed).get_verify_key_hex()
    -                    if (user and hexkey != user["pubkey"]) or not self._check_keys(hexkey, user_app):
    -                        raise Exception
    -                    else:
    -                        return True
    -                except Exception:
    -                    choice = ask_choice(
    -                        "\nSeems one or more more words entered is invalid.\nWhat would you like to do? ", CHOICES,
    -                    )
    -                    if choice == RESTART_CHOICE:
    -                        return False
    -                    fill_words()
    -
    -        while True:
    -            if _fill_identity_args(identity, explorer):
    -                identity_info = IdentityInfo(self.identity, self.email, self.words, self.explorer)
    -                return identity_info
    -
    -

    Methods

    -
    -
    -def ask_identity(self, identity=None, explorer=None) -
    -
    -
    -
    - -Expand source code - -
    def ask_identity(self, identity=None, explorer=None):
    -    def _fill_identity_args(identity, explorer):
    -        def fill_words():
    -            words = ask_string("Copy the phrase from your 3bot Connect app here: ")
    -            self.words = words
    -
    -        def fill_identity():
    -            identity = ask_string("what is your threebot name (identity)? ")
    -            if "." not in identity:
    -                identity += ".3bot"
    -            self.identity = identity
    -
    -        if identity:
    -            if self.identity != identity and self.identity:
    -                self.reset()
    -            self.identity = identity
    -
    -        if explorer:
    -            self.explorer = explorer
    -        elif not self.explorer:
    -            response = ask_choice(
    -                "Which network would you like to register to? ", ["mainnet", "testnet", "devnet", "none"]
    -            )
    -            self.explorer = NETWORKS.get(response, None)
    -        if not self.explorer:
    -            return True
    -
    -        user, user_app = None, None
    -        while not user:
    -            fill_identity()
    -            try:
    -                user, user_app = self._get_user()
    -            except exceptions.Value as e:
    -                response = ask_choice(f"{e}What would you like to do? ", CHOICES)
    -                if response == RESTART_CHOICE:
    -                    return False
    -
    -        while not self.email:
    -            self.email = ask_string("What is the email address associated with your identity? ")
    -            if self._check_email(self.email):
    -                break
    -            else:
    -                self.email = None
    -                response = ask_choice(
    -                    "This email is currently associated with another identity. What would you like to do? ",
    -                    CHOICES,
    -                )
    -                if response == RESTART_CHOICE:
    -                    return False
    -
    -        print("Configured email for this identity is {}".format(self.email))
    -
    -        # time to do validation of words
    -        hexkey = None
    -        while True:
    -            if not self.words:
    -                fill_words()
    -            try:
    -                seed = mnemonic.mnemonic_to_key(self.words.strip())
    -                hexkey = NACL(seed).get_verify_key_hex()
    -                if (user and hexkey != user["pubkey"]) or not self._check_keys(hexkey, user_app):
    -                    raise Exception
    -                else:
    -                    return True
    -            except Exception:
    -                choice = ask_choice(
    -                    "\nSeems one or more more words entered is invalid.\nWhat would you like to do? ", CHOICES,
    -                )
    -                if choice == RESTART_CHOICE:
    -                    return False
    -                fill_words()
    -
    -    while True:
    -        if _fill_identity_args(identity, explorer):
    -            identity_info = IdentityInfo(self.identity, self.email, self.words, self.explorer)
    -            return identity_info
    -
    -
    -
    -def reset(self) -
    -
    -
    -
    - -Expand source code - -
    def reset(self):
    -    self.identity = ""
    -    self.email = ""
    -    self.words = ""
    -    self.explorer = ""
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - diff --git a/docs/api/jumpscale/threesdk/index.html b/docs/api/jumpscale/threesdk/index.html deleted file mode 100644 index 6cf3f611c..000000000 --- a/docs/api/jumpscale/threesdk/index.html +++ /dev/null @@ -1,518 +0,0 @@ - - - - - - -jumpscale.threesdk API documentation - - - - - - - - - - - -
    -
    -
    -

    Module jumpscale.threesdk

    -
    -
    -
    - -Expand source code - -
    from .threebot import ThreeBot as threebot
    -import inspect
    -import cgi
    -from prompt_toolkit.formatted_text import HTML
    -from prompt_toolkit.shortcuts import print_formatted_text
    -
    -__all__ = ["threebot", "info"]
    -
    -
    -def info():
    -    print_formatted_text(HTML(_get_doc(__all__)))
    -
    -
    -def _get_doc_line(doc):
    -    if not doc:
    -        return ""
    -    for line in doc.splitlines():
    -        if line.strip():
    -            return line.strip()
    -    return ""
    -
    -
    -def _get_doc(root_module, level=0, size=4):
    -    """get a formatted docstring from a module
    -    this will loop over __all__self.
    -
    -    :param root_module: root module
    -    :type root_module: module
    -    :param level: spacing level, defaults to 0
    -    :type level: int, optional
    -    :param level: spacing size, defaults to 4
    -    :type level: int, optional
    -    :return: docstring
    -    :rtype: str
    -    """
    -
    -    doc = ""
    -
    -    if isinstance(root_module, list):
    -        glob = globals()
    -        members = [(name, glob[name]) for name in root_module]
    -    else:
    -        members = inspect.getmembers(root_module)
    -    for name, obj in members:
    -        if name.startswith("_"):
    -            continue
    -        if name[0].lower() != name[0]:
    -            continue
    -
    -        is_module = not inspect.isroutine(obj)
    -        if is_module and level != 0:
    -            continue
    -
    -        spaces = " " * level
    -
    -        if is_module:
    -            doc += f"{spaces}<ansibrightblue>{name}</ansibrightblue>"
    -        elif getattr(obj, "__property__", False):
    -            doc += f"{spaces}<ansicyan>{name}</ansicyan>"
    -        else:
    -            doc += f"{spaces}<ansigreen>{name}</ansigreen>"
    -
    -        if obj.__doc__:
    -            try:
    -                # only get first line of member docstring
    -                first_line = _get_doc_line(obj.__doc__)
    -                doc += cgi.html.escape(f": {first_line}")
    -            except IndexError:
    -                pass
    -
    -        doc = f"{doc}\n"
    -
    -        if is_module:
    -            doc += _get_doc(obj, level=level + size)
    -
    -    return doc
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.threesdk.container
    -
    -
    -
    -
    jumpscale.threesdk.identitymanager
    -
    -
    -
    -
    jumpscale.threesdk.settings
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def info() -
    -
    -
    -
    - -Expand source code - -
    def info():
    -    print_formatted_text(HTML(_get_doc(__all__)))
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class threebot -
    -
    -

    Manage your threebot

    -
    - -Expand source code - -
    class ThreeBot(Container):
    -    """
    -    Manage your threebot
    -    """
    -
    -    @staticmethod
    -    def install(
    -        name=None, image=None, identity=None, email=None, words=None, explorer=None, development: bool = None,
    -    ):
    -        """Creates a threebot container
    -
    -        Args:
    -            name (str, optional): name of the container. Defaults to 3bot-ng
    -            image (str, optional): container image. Defaults to "threefoldtech/js-ng:latest".
    -            identity (str, optional): threebot name. Defaults to None.
    -            email (str, optional): threebot email. Defaults to None.
    -            words (str, optional): seed phrase of the user. Defaults to None.
    -            explorer (str, optional): which explorer network to use: mainnet, testnet, devnet. Defaults to None.
    -            development (bool, optional): if true will mount codedir. Defaults to False.
    -
    -        Raises:
    -            Value: Container with specified name already exists
    -            Value: explorer not in mainnet, testnet, devnet
    -        """
    -        if development is None:
    -            development = settings.expert
    -        name = name or DEFAULT_CONTAINER_NAME
    -        current_version = get_current_version()
    -        image = image or f"{DEFAULT_IMAGE}:{current_version}"
    -
    -        pers_path = f"{PERSISTENT_STORE}/{name}"
    -        configure = not os.path.exists(pers_path)
    -        if configure:
    -            identity = IdentityManager(identity, email, words, explorer)
    -            identity, email, words, explorer = identity.ask_identity()
    -
    -        os.makedirs(PERSISTENT_STORE, exist_ok=True)
    -        volumes = {pers_path: {"bind": "/root/.config/jumpscale", "mode": "rw"}}
    -
    -        container = Container.install(name, image, development, volumes)
    -        container.exec_run(["redis-server", "--daemonize yes"])
    -
    -        if configure:
    -            container.exec_run(["jsng", f"j.core.identity.new('default', '{identity}', '{email}', '{words}')"])
    -            container.exec_run(["jsng", "j.core.identity.set_default('default')"])
    -
    -    @staticmethod
    -    def jsng(name=DEFAULT_CONTAINER_NAME):
    -        """Get's shell in threebot
    -
    -        Args:
    -            name (str): name of the container (default: 3bot-ng)
    -        """
    -        Container.exec(name, "jsng")
    -
    -    @staticmethod
    -    def shell(name=DEFAULT_CONTAINER_NAME):
    -        """Get's shell in threebot
    -
    -        Args:
    -            name (str): name of the container (default: 3bot-ng)
    -        """
    -        Container.exec(name, "bash")
    -
    -    @staticmethod
    -    def start(name=DEFAULT_CONTAINER_NAME):
    -        """Start threebot container with threebot server
    -
    -        Args:
    -            name (str): name of the container (default: 3bot-ng)
    -        """
    -        Container.start(name)
    -        Container.exec(name, ["threebot", "start", "--background"])
    -
    -    @staticmethod
    -    def stop(name=DEFAULT_CONTAINER_NAME):
    -        """Stop threebot installation with container
    -
    -        Args:
    -            name (str): name of the container (default: 3bot-ng)
    -        """
    -        if name in docker_client.list():
    -            Container.exec(name, ["threebot", "stop"])
    -            Container.stop(name)
    -        else:
    -            print("Container is already stopped")
    -
    -    @staticmethod
    -    def restart(name=DEFAULT_CONTAINER_NAME):
    -        """restart threebot installation with container
    -
    -        Args:
    -            name (str): name of the container (default: 3bot-ng)
    -        """
    -        ThreeBot.stop(name=name)
    -        ThreeBot.start(name=name)
    -
    -

    Ancestors

    - -

    Static methods

    -
    -
    -def install(name=None, image=None, identity=None, email=None, words=None, explorer=None, development: bool = None) -
    -
    -

    Creates a threebot container

    -

    Args

    -
    -
    name : str, optional
    -
    name of the container. Defaults to 3bot-ng
    -
    image : str, optional
    -
    container image. Defaults to "threefoldtech/js-ng:latest".
    -
    identity : str, optional
    -
    threebot name. Defaults to None.
    -
    email : str, optional
    -
    threebot email. Defaults to None.
    -
    words : str, optional
    -
    seed phrase of the user. Defaults to None.
    -
    explorer : str, optional
    -
    which explorer network to use: mainnet, testnet, devnet. Defaults to None.
    -
    development : bool, optional
    -
    if true will mount codedir. Defaults to False.
    -
    -

    Raises

    -
    -
    Value
    -
    Container with specified name already exists
    -
    Value
    -
    explorer not in mainnet, testnet, devnet
    -
    -
    - -Expand source code - -
    @staticmethod
    -def install(
    -    name=None, image=None, identity=None, email=None, words=None, explorer=None, development: bool = None,
    -):
    -    """Creates a threebot container
    -
    -    Args:
    -        name (str, optional): name of the container. Defaults to 3bot-ng
    -        image (str, optional): container image. Defaults to "threefoldtech/js-ng:latest".
    -        identity (str, optional): threebot name. Defaults to None.
    -        email (str, optional): threebot email. Defaults to None.
    -        words (str, optional): seed phrase of the user. Defaults to None.
    -        explorer (str, optional): which explorer network to use: mainnet, testnet, devnet. Defaults to None.
    -        development (bool, optional): if true will mount codedir. Defaults to False.
    -
    -    Raises:
    -        Value: Container with specified name already exists
    -        Value: explorer not in mainnet, testnet, devnet
    -    """
    -    if development is None:
    -        development = settings.expert
    -    name = name or DEFAULT_CONTAINER_NAME
    -    current_version = get_current_version()
    -    image = image or f"{DEFAULT_IMAGE}:{current_version}"
    -
    -    pers_path = f"{PERSISTENT_STORE}/{name}"
    -    configure = not os.path.exists(pers_path)
    -    if configure:
    -        identity = IdentityManager(identity, email, words, explorer)
    -        identity, email, words, explorer = identity.ask_identity()
    -
    -    os.makedirs(PERSISTENT_STORE, exist_ok=True)
    -    volumes = {pers_path: {"bind": "/root/.config/jumpscale", "mode": "rw"}}
    -
    -    container = Container.install(name, image, development, volumes)
    -    container.exec_run(["redis-server", "--daemonize yes"])
    -
    -    if configure:
    -        container.exec_run(["jsng", f"j.core.identity.new('default', '{identity}', '{email}', '{words}')"])
    -        container.exec_run(["jsng", "j.core.identity.set_default('default')"])
    -
    -
    -
    -def jsng(name='3bot-ng') -
    -
    -

    Get's shell in threebot

    -

    Args

    -
    -
    name : str
    -
    name of the container (default: 3bot-ng)
    -
    -
    - -Expand source code - -
    @staticmethod
    -def jsng(name=DEFAULT_CONTAINER_NAME):
    -    """Get's shell in threebot
    -
    -    Args:
    -        name (str): name of the container (default: 3bot-ng)
    -    """
    -    Container.exec(name, "jsng")
    -
    -
    -
    -def restart(name='3bot-ng') -
    -
    -

    restart threebot installation with container

    -

    Args

    -
    -
    name : str
    -
    name of the container (default: 3bot-ng)
    -
    -
    - -Expand source code - -
    @staticmethod
    -def restart(name=DEFAULT_CONTAINER_NAME):
    -    """restart threebot installation with container
    -
    -    Args:
    -        name (str): name of the container (default: 3bot-ng)
    -    """
    -    ThreeBot.stop(name=name)
    -    ThreeBot.start(name=name)
    -
    -
    -
    -def shell(name='3bot-ng') -
    -
    -

    Get's shell in threebot

    -

    Args

    -
    -
    name : str
    -
    name of the container (default: 3bot-ng)
    -
    -
    - -Expand source code - -
    @staticmethod
    -def shell(name=DEFAULT_CONTAINER_NAME):
    -    """Get's shell in threebot
    -
    -    Args:
    -        name (str): name of the container (default: 3bot-ng)
    -    """
    -    Container.exec(name, "bash")
    -
    -
    -
    -def start(name='3bot-ng') -
    -
    -

    Start threebot container with threebot server

    -

    Args

    -
    -
    name : str
    -
    name of the container (default: 3bot-ng)
    -
    -
    - -Expand source code - -
    @staticmethod
    -def start(name=DEFAULT_CONTAINER_NAME):
    -    """Start threebot container with threebot server
    -
    -    Args:
    -        name (str): name of the container (default: 3bot-ng)
    -    """
    -    Container.start(name)
    -    Container.exec(name, ["threebot", "start", "--background"])
    -
    -
    -
    -def stop(name='3bot-ng') -
    -
    -

    Stop threebot installation with container

    -

    Args

    -
    -
    name : str
    -
    name of the container (default: 3bot-ng)
    -
    -
    - -Expand source code - -
    @staticmethod
    -def stop(name=DEFAULT_CONTAINER_NAME):
    -    """Stop threebot installation with container
    -
    -    Args:
    -        name (str): name of the container (default: 3bot-ng)
    -    """
    -    if name in docker_client.list():
    -        Container.exec(name, ["threebot", "stop"])
    -        Container.stop(name)
    -    else:
    -        print("Container is already stopped")
    -
    -
    -
    -

    Inherited members

    - -
    -
    -
    -
    - -
    - - - diff --git a/docs/api/jumpscale/threesdk/settings.html b/docs/api/jumpscale/threesdk/settings.html deleted file mode 100644 index 02559f717..000000000 --- a/docs/api/jumpscale/threesdk/settings.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - -jumpscale.threesdk.settings API documentation - - - - - - - - - - - -
    -
    -
    -

    Module jumpscale.threesdk.settings

    -
    -
    -
    - -Expand source code - -
    expert = False
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - diff --git a/docs/api/jumpscale/tools/console/console.html b/docs/api/jumpscale/tools/console/console.html deleted file mode 100644 index 7ffb52530..000000000 --- a/docs/api/jumpscale/tools/console/console.html +++ /dev/null @@ -1,689 +0,0 @@ - - - - - - -jumpscale.tools.console.console API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.console.console

    -
    -
    -

    Console module helps with coloring in the console and asking for input from the user.

    -
    JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")                                                                     
    -JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")                                                              
    -
    -
    -Source code -
    """Console module helps with coloring in the console and asking for input from the user.
    -```
    -JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")                                                                     
    -JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")                                                              
    -```
    -
    -"""
    -
    -
    -import colorama
    -import getpass
    -import os
    -
    -<<<<<<< HEAD
    -"""
    -JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")                                                                     
    -JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")                                                              
    -"""
    -=======
    ->>>>>>> development
    -
    -NAMES_TO_COLORS = {}
    -for attrname in dir(colorama.Fore):
    -    if attrname.isupper():
    -        NAMES_TO_COLORS[attrname] = getattr(colorama.Fore, attrname)
    -
    -for attrname in dir(colorama.Back):
    -    if attrname.isupper():
    -        NAMES_TO_COLORS["BG" + attrname] = getattr(colorama.Back, attrname)
    -
    -NAMES_TO_COLORS["RESET"] = colorama.Style.RESET_ALL
    -
    -
    -def format(s):
    -    return s.format(**NAMES_TO_COLORS)
    -
    -
    -def printcolors(s):
    -    """
    -        >>> j.tools.console.printcolors("{RED}Hello world")
    -        Hello world
    -        >>> j.tools.console.printcolors("{GREEN}Hello world")
    -        Hello world
    -
    -        Arguments:
    -        s {[type]} -- [description]
    -        """
    -    print(format(s))
    -
    -
    -def ask_password(prompt="Password : ", forbiddens=[]):
    -    """Prompt the user for a password without echoing
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Password : "})
    -        forbiddens {list} -- the list of bad passwords (default: {[]})
    -    
    -    Returns:
    -        str -- the appropriate input password
    -    """
    -    password = getpass.getpass(prompt)
    -    if password not in forbiddens:
    -        return password
    -    else:
    -        return ask_password(prompt, forbiddens)
    -
    -
    -def ask_yes_no(prompt="[y/n] :", default="y", valid=["y", "n"]):
    -    """Display a yes/no question and loop until a valid answer is entered
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {'[y/n] :'})
    -        default {str} -- the default answer if there is no answer (default: {"y"})
    -        valid {list} -- the list of appropriate answers (default: {["y", "n"]})
    -    
    -    Returns:
    -        str -- the answer
    -    """
    -
    -    answer = input(prompt)
    -    if answer in valid:
    -        return answer
    -    elif answer == "":
    -        return default
    -    else:
    -        return ask_yes_no(prompt, default, valid)
    -
    -
    -def ask_int(prompt="Type int :"):
    -    try:
    -        return int(input(prompt))
    -    except ValueError:
    -        return ask_int(prompt)
    -
    -
    -def ask_int_in_range(mini, maxi, prompt="Type int :"):
    -    """Get an integer response between two integer on asked question
    -    
    -    Arguments:
    -        mini {int} -- the minimum value for the number
    -        maxi {int} -- the maximum value for the number
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type int :"})
    -    
    -    Returns:
    -        int -- the input number on the range provided
    -    """
    -    try:
    -        answer = int(input(prompt))
    -        if mini <= answer <= maxi:
    -            return answer
    -        else:
    -            return ask_int_in_range(mini, maxi, prompt)
    -    except ValueError:
    -        return ask_int_in_range(mini, maxi, prompt)
    -
    -
    -def ask_float(prompt="Type float :"):
    -    try:
    -        return float(input(prompt))
    -    except ValueError:
    -        return ask_float(prompt)
    -
    -
    -def ask_float_in_range(mini, maxi, prompt="Type float :"):
    -    """Get an float response between two float on asked question
    -    
    -    Arguments:
    -        mini {float} -- the minimum value for the number
    -        maxi {float} -- the maximum value for the number
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type float :"})
    -    
    -    Returns:
    -        float -- the input number on the range provided
    -    """
    -    try:
    -        answer = float(input(prompt))
    -        if mini <= answer <= maxi:
    -            return answer
    -        else:
    -            return ask_float_in_range(mini, maxi, prompt)
    -    except ValueError:
    -        return ask_float_in_range(mini, maxi, prompt)
    -
    -
    -def _print_choices(choices_list):
    -    """Helper function : clear screen and print the choices in numbers"""
    -    os.system("clear")
    -    number = 0
    -    for choice in choices_list:
    -        number += 1
    -        print(f"{number}. " + choice)
    -
    -
    -def ask_choice(prompt="Type choice number : ", choices_list=[]):
    -    """Get an option from provided list
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type choice number : "})
    -        choices_list {list} -- the available options (default: {[]})
    -    
    -    Returns:
    -        str -- the chosen option
    -    """
    -    _print_choices(choices_list)
    -    answer = input(prompt)
    -    try:
    -        return choices_list[int(answer) - 1]
    -    except (IndexError, ValueError):
    -        return ask_choice(prompt, choices_list)
    -
    -
    -def ask_multi_choices(prompt="Add to choices : ", choices_list=[], to_save="s", to_quit="q"):
    -    """Collect multi choices from list
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question method (default: {"Add to choices : "})
    -        choices_list {list} -- the available options (default: {[]})
    -        to_save {str} -- escape and save choices (default: {"s"})
    -        to_quit {str} -- escape without saving (default: {"q"})
    -    
    -    Returns:
    -        list -- list of the selected choices
    -    """
    -    selected_choices = []
    -    print(f"'{to_save}' to save and '{to_quit}' to quit")
    -    _print_choices(choices_list)
    -
    -    while True:
    -        answer = input(prompt)
    -        if answer == to_quit:
    -            return []
    -        elif answer == to_save or answer == "":
    -            return selected_choices
    -        else:
    -            try:
    -                selected_choices.append(choices_list[int(answer) - 1])
    -            except (IndexError, ValueError):
    -                return ask_multi_choices(prompt, choices_list, to_save, to_quit)
    -
    -
    -def ask_multi_lines(prompt="Type :", escape_string="."):
    -    """Get input from user provided multilines
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type :"})
    -        escape_string {str} -- escape character (default: {"."})
    -    
    -    Returns:
    -        str -- the text seperated by lines
    -    """
    -    text = []
    -    user_input = input(prompt)
    -    while user_input != escape_string:
    -        text.append(user_input)
    -        user_input = input(prompt)
    -    return "\n".join(text)
    -
    -
    -def ask_string(prompt="Type :"):
    -    """Just input function
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type :"})
    -    
    -    Returns:
    -        str -- the string input
    -    """
    -    return input(prompt)
    -
    -    print(format(s))
    -
    -
    -def printobj(obj):
    -    from pprint import pprint
    -
    -    pprint(obj)
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def ask_choice(prompt='Type choice number : ', choices_list=[]) -
    -
    -

    Get an option from provided list

    -

    Keyword Arguments: -prompt {str} – the question message (default: {"Type choice number : "}) -choices_list {list} – the available options (default: {[]})

    -

    Returns

    -
    -
    strthe chosen option
    -
     
    -
    -
    -Source code -
    def ask_choice(prompt="Type choice number : ", choices_list=[]):
    -    """Get an option from provided list
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type choice number : "})
    -        choices_list {list} -- the available options (default: {[]})
    -    
    -    Returns:
    -        str -- the chosen option
    -    """
    -    _print_choices(choices_list)
    -    answer = input(prompt)
    -    try:
    -        return choices_list[int(answer) - 1]
    -    except (IndexError, ValueError):
    -        return ask_choice(prompt, choices_list)
    -
    -
    -
    -def ask_float(prompt='Type float :') -
    -
    -
    -
    -Source code -
    def ask_float(prompt="Type float :"):
    -    try:
    -        return float(input(prompt))
    -    except ValueError:
    -        return ask_float(prompt)
    -
    -
    -
    -def ask_float_in_range(mini, maxi, prompt='Type float :') -
    -
    -

    Get an float response between two float on asked question

    -

    Arguments

    -

    mini {float} – the minimum value for the number -maxi {float} – the maximum value for the number -Keyword Arguments: -prompt {str} – the question message (default: {"Type float :"})

    -

    Returns

    -
    -
    floatthe input number on the range provided
    -
     
    -
    -
    -Source code -
    def ask_float_in_range(mini, maxi, prompt="Type float :"):
    -    """Get an float response between two float on asked question
    -    
    -    Arguments:
    -        mini {float} -- the minimum value for the number
    -        maxi {float} -- the maximum value for the number
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type float :"})
    -    
    -    Returns:
    -        float -- the input number on the range provided
    -    """
    -    try:
    -        answer = float(input(prompt))
    -        if mini <= answer <= maxi:
    -            return answer
    -        else:
    -            return ask_float_in_range(mini, maxi, prompt)
    -    except ValueError:
    -        return ask_float_in_range(mini, maxi, prompt)
    -
    -
    -
    -def ask_int(prompt='Type int :') -
    -
    -
    -
    -Source code -
    def ask_int(prompt="Type int :"):
    -    try:
    -        return int(input(prompt))
    -    except ValueError:
    -        return ask_int(prompt)
    -
    -
    -
    -def ask_int_in_range(mini, maxi, prompt='Type int :') -
    -
    -

    Get an integer response between two integer on asked question

    -

    Arguments

    -

    mini {int} – the minimum value for the number -maxi {int} – the maximum value for the number -Keyword Arguments: -prompt {str} – the question message (default: {"Type int :"})

    -

    Returns

    -
    -
    intthe input number on the range provided
    -
     
    -
    -
    -Source code -
    def ask_int_in_range(mini, maxi, prompt="Type int :"):
    -    """Get an integer response between two integer on asked question
    -    
    -    Arguments:
    -        mini {int} -- the minimum value for the number
    -        maxi {int} -- the maximum value for the number
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type int :"})
    -    
    -    Returns:
    -        int -- the input number on the range provided
    -    """
    -    try:
    -        answer = int(input(prompt))
    -        if mini <= answer <= maxi:
    -            return answer
    -        else:
    -            return ask_int_in_range(mini, maxi, prompt)
    -    except ValueError:
    -        return ask_int_in_range(mini, maxi, prompt)
    -
    -
    -
    -def ask_multi_choices(prompt='Add to choices : ', choices_list=[], to_save='s', to_quit='q') -
    -
    -

    Collect multi choices from list

    -

    Keyword Arguments: -prompt {str} – the question method (default: {"Add to choices : "}) -choices_list {list} – the available options (default: {[]}) -to_save {str} – escape and save choices (default: {"s"}) -to_quit {str} – escape without saving (default: {"q"})

    -

    Returns

    -
    -
    listlist of the selected choices
    -
     
    -
    -
    -Source code -
    def ask_multi_choices(prompt="Add to choices : ", choices_list=[], to_save="s", to_quit="q"):
    -    """Collect multi choices from list
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question method (default: {"Add to choices : "})
    -        choices_list {list} -- the available options (default: {[]})
    -        to_save {str} -- escape and save choices (default: {"s"})
    -        to_quit {str} -- escape without saving (default: {"q"})
    -    
    -    Returns:
    -        list -- list of the selected choices
    -    """
    -    selected_choices = []
    -    print(f"'{to_save}' to save and '{to_quit}' to quit")
    -    _print_choices(choices_list)
    -
    -    while True:
    -        answer = input(prompt)
    -        if answer == to_quit:
    -            return []
    -        elif answer == to_save or answer == "":
    -            return selected_choices
    -        else:
    -            try:
    -                selected_choices.append(choices_list[int(answer) - 1])
    -            except (IndexError, ValueError):
    -                return ask_multi_choices(prompt, choices_list, to_save, to_quit)
    -
    -
    -
    -def ask_multi_lines(prompt='Type :', escape_string='.') -
    -
    -

    Get input from user provided multilines

    -

    Keyword Arguments: -prompt {str} – the question message (default: {"Type :"}) -escape_string {str} – escape character (default: {"."})

    -

    Returns

    -
    -
    strthe text seperated by lines
    -
     
    -
    -
    -Source code -
    def ask_multi_lines(prompt="Type :", escape_string="."):
    -    """Get input from user provided multilines
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type :"})
    -        escape_string {str} -- escape character (default: {"."})
    -    
    -    Returns:
    -        str -- the text seperated by lines
    -    """
    -    text = []
    -    user_input = input(prompt)
    -    while user_input != escape_string:
    -        text.append(user_input)
    -        user_input = input(prompt)
    -    return "\n".join(text)
    -
    -
    -
    -def ask_password(prompt='Password : ', forbiddens=[]) -
    -
    -

    Prompt the user for a password without echoing

    -

    Keyword Arguments: -prompt {str} – the question message (default: {"Password : "}) -forbiddens {list} – the list of bad passwords (default: {[]})

    -

    Returns

    -
    -
    strthe appropriate input password
    -
     
    -
    -
    -Source code -
    def ask_password(prompt="Password : ", forbiddens=[]):
    -    """Prompt the user for a password without echoing
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Password : "})
    -        forbiddens {list} -- the list of bad passwords (default: {[]})
    -    
    -    Returns:
    -        str -- the appropriate input password
    -    """
    -    password = getpass.getpass(prompt)
    -    if password not in forbiddens:
    -        return password
    -    else:
    -        return ask_password(prompt, forbiddens)
    -
    -
    -
    -def ask_string(prompt='Type :') -
    -
    -

    Just input function

    -

    Keyword Arguments: -prompt {str} – the question message (default: {"Type :"})

    -

    Returns

    -
    -
    strthe string input
    -
     
    -
    -
    -Source code -
    def ask_string(prompt="Type :"):
    -    """Just input function
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {"Type :"})
    -    
    -    Returns:
    -        str -- the string input
    -    """
    -    return input(prompt)
    -
    -    print(format(s))
    -
    -
    -
    -def ask_yes_no(prompt='[y/n] :', default='y', valid=['y', 'n']) -
    -
    -

    Display a yes/no question and loop until a valid answer is entered

    -

    Keyword Arguments: -prompt {str} – the question message (default: {'[y/n] :'}) -default {str} – the default answer if there is no answer (default: {"y"}) -valid {list} – the list of appropriate answers (default: {["y", "n"]})

    -

    Returns

    -
    -
    strthe answer
    -
     
    -
    -
    -Source code -
    def ask_yes_no(prompt="[y/n] :", default="y", valid=["y", "n"]):
    -    """Display a yes/no question and loop until a valid answer is entered
    -    
    -    Keyword Arguments:
    -        prompt {str} -- the question message (default: {'[y/n] :'})
    -        default {str} -- the default answer if there is no answer (default: {"y"})
    -        valid {list} -- the list of appropriate answers (default: {["y", "n"]})
    -    
    -    Returns:
    -        str -- the answer
    -    """
    -
    -    answer = input(prompt)
    -    if answer in valid:
    -        return answer
    -    elif answer == "":
    -        return default
    -    else:
    -        return ask_yes_no(prompt, default, valid)
    -
    -
    -
    -def format(s) -
    -
    -
    -
    -Source code -
    def format(s):
    -    return s.format(**NAMES_TO_COLORS)
    -
    -
    -
    -def printcolors(s) -
    -
    -
    >>> j.tools.console.printcolors("{RED}Hello world")
    -Hello world
    ->>> j.tools.console.printcolors("{GREEN}Hello world")
    -Hello world
    -
    -

    Arguments: -s {[type]} – [description]

    -
    -Source code -
    def printcolors(s):
    -    """
    -        >>> j.tools.console.printcolors("{RED}Hello world")
    -        Hello world
    -        >>> j.tools.console.printcolors("{GREEN}Hello world")
    -        Hello world
    -
    -        Arguments:
    -        s {[type]} -- [description]
    -        """
    -    print(format(s))
    -
    -
    -
    -def printobj(obj) -
    -
    -
    -
    -Source code -
    def printobj(obj):
    -    from pprint import pprint
    -
    -    pprint(obj)
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/tools/dnstool/index.html b/docs/api/jumpscale/tools/dnstool/index.html deleted file mode 100644 index 1a0e1883e..000000000 --- a/docs/api/jumpscale/tools/dnstool/index.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - -jumpscale.tools.dnstool API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.dnstool

    -
    -
    -

    Helper to get nameservers information and resolving domains.

    -
    -Source code -
    """
    -Helper to get nameservers information and resolving domains.
    -
    -"""
    -# TODO: update code
    -
    -
    -try:
    -    import dns
    -    import dns.message
    -    import dns.rdataclass
    -    import dns.rdatatype
    -    import dns.query
    -    import dns.resolver
    -
    -except ImportError as e:
    -    print("WARNING install dnspython: 'pip3 install dnspython'")
    -
    -
    -class DNSClient:
    -    def __init__(self, nameservers=None, port=53):
    -        self.nameservers = nameservers or ["8.8.8.8", "8.8.4.4"]
    -        if "localhost" in self.nameservers:
    -            nameservers.pop(nameservers.index("localhost"))
    -            nameservers.append("127.0.0.1")
    -        self.resolver = dns.resolver.Resolver(configure=False)
    -        self.resolver.nameservers = self.nameservers
    -        self.resolver.port = port
    -
    -    def nameservers_get(self, domain="threefoldtoken.org"):
    -        answer = self.resolver.query(domain, "NS")
    -
    -        res = []
    -        for rr in answer:
    -            res.append(rr.target.to_text())
    -        return res
    -
    -    def namerecords_get(self, url="www.threefoldtoken.org"):
    -        """
    -        return ip addr for a full name
    -        """
    -        answer = self.resolver.query(url, "A")
    -
    -        res = []
    -        for rr in answer:
    -            res.append(rr.address)
    -        return res
    -
    -
    -resolver = DNSClient()
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class DNSClient -(nameservers=None, port=53) -
    -
    -
    -
    -Source code -
    class DNSClient:
    -    def __init__(self, nameservers=None, port=53):
    -        self.nameservers = nameservers or ["8.8.8.8", "8.8.4.4"]
    -        if "localhost" in self.nameservers:
    -            nameservers.pop(nameservers.index("localhost"))
    -            nameservers.append("127.0.0.1")
    -        self.resolver = dns.resolver.Resolver(configure=False)
    -        self.resolver.nameservers = self.nameservers
    -        self.resolver.port = port
    -
    -    def nameservers_get(self, domain="threefoldtoken.org"):
    -        answer = self.resolver.query(domain, "NS")
    -
    -        res = []
    -        for rr in answer:
    -            res.append(rr.target.to_text())
    -        return res
    -
    -    def namerecords_get(self, url="www.threefoldtoken.org"):
    -        """
    -        return ip addr for a full name
    -        """
    -        answer = self.resolver.query(url, "A")
    -
    -        res = []
    -        for rr in answer:
    -            res.append(rr.address)
    -        return res
    -
    -

    Methods

    -
    -
    -def namerecords_get(self, url='www.threefoldtoken.org') -
    -
    -

    return ip addr for a full name

    -
    -Source code -
    def namerecords_get(self, url="www.threefoldtoken.org"):
    -    """
    -    return ip addr for a full name
    -    """
    -    answer = self.resolver.query(url, "A")
    -
    -    res = []
    -    for rr in answer:
    -        res.append(rr.address)
    -    return res
    -
    -
    -
    -def nameservers_get(self, domain='threefoldtoken.org') -
    -
    -
    -
    -Source code -
    def nameservers_get(self, domain="threefoldtoken.org"):
    -    answer = self.resolver.query(domain, "NS")
    -
    -    res = []
    -    for rr in answer:
    -        res.append(rr.target.to_text())
    -    return res
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/tools/imagelib/imagelib.html b/docs/api/jumpscale/tools/imagelib/imagelib.html deleted file mode 100644 index 46d46768c..000000000 --- a/docs/api/jumpscale/tools/imagelib/imagelib.html +++ /dev/null @@ -1,302 +0,0 @@ - - - - - - -jumpscale.tools.imagelib.imagelib API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.imagelib.imagelib

    -
    -
    -
    -Source code -
    import os
    -from PIL import Image
    -
    -
    -def get_list_files(dir_name):
    -    """returns a list of directories for all image files in a root folder
    -
    -    Arguments:
    -        dir_name (str) : the directory of the root folder
    -
    -    Returns:
    -        all_files (list) : the list of directories for all files in the root folder
    -    """
    -    # create a list of file and sub directories
    -    # names in the given directory
    -    files_list = os.listdir(dir_name)
    -    all_files = list()
    -    # Iterate over all the entries
    -    for entry in files_list:
    -        # Create full path
    -        full_path = os.path.join(dir_name, entry)
    -        # If entry is a directory then get the list of files in this directory
    -        if os.path.isdir(full_path):
    -            all_files = all_files + get_list_files(full_path)
    -        else:
    -            path_split = os.path.splitext(full_path)
    -            if path_split[1] in [".jpg", ".jpeg", ".png"]:
    -                all_files.append(full_path)
    -
    -    return all_files
    -
    -
    -def get_image(path):
    -    """returns an PIL.Image object by path
    -    
    -    Arguments:
    -        path (string) : the image path
    -    
    -    Returns:
    -        PIL.Image object
    -    """
    -    return Image.open(path)
    -
    -
    -def resize(path, pathnew, width=1024):
    -    """resize an image 
    -    
    -    Arguments:
    -        path (string) : the path of the image will be resized
    -        pathnew (string) : the path of new resized image
    -    
    -    Keyword Arguments:
    -        width (int) : the width of the new image , hieght is depending on it
    -    
    -    Returns:
    -        (string) : True if image resized successfully or the exception message if not
    -    """
    -    im = get_image(path)
    -    xnew = width
    -    x, y = im.size
    -    ynew = int(float(y) / (float(x) / float(xnew)))
    -    imnew = im.resize((xnew, ynew), Image.ANTIALIAS)
    -    try:
    -        imnew.save(pathnew)
    -        return "True"
    -    except Exception as e:
    -        return str(e)
    -
    -
    -def resize_images_in_dir(folder, foldernew, width=1024):
    -    """resize images in folder
    -    
    -    Arguments:
    -        folder (string) : the path of the root folder in which images will be resized
    -        foldernew (string) : the path of the folder in which new images will be stored
    -    
    -    Keyword Arguments:
    -        width (int) : the width of the new images , hieght is depending on it
    -    
    -    Returns:
    -        (dict) : a dict with img path as key and its statue as value
    -            statue : True if image resized successfully or the exception message if not
    -    """
    -    img_list = get_list_files(folder)
    -    print(img_list)
    -    img_statue = {}
    -    for img in img_list:
    -        statue = resize(img, os.path.join(foldernew, os.path.basename(img)), width=width)
    -        img_statue[img] = statue
    -    return img_statue
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def get_image(path) -
    -
    -

    returns an PIL.Image object by path

    -

    Arguments

    -

    path (string) : the image path

    -

    Returns

    -
    -
    PIL.Image object
    -
     
    -
    -
    -Source code -
    def get_image(path):
    -    """returns an PIL.Image object by path
    -    
    -    Arguments:
    -        path (string) : the image path
    -    
    -    Returns:
    -        PIL.Image object
    -    """
    -    return Image.open(path)
    -
    -
    -
    -def get_list_files(dir_name) -
    -
    -

    returns a list of directories for all image files in a root folder

    -

    Arguments

    -

    dir_name (str) : the directory of the root folder

    -

    Returns

    -
    -
    all_files (list) : the list of directories for all files in the root folder
    -
     
    -
    -
    -Source code -
    def get_list_files(dir_name):
    -    """returns a list of directories for all image files in a root folder
    -
    -    Arguments:
    -        dir_name (str) : the directory of the root folder
    -
    -    Returns:
    -        all_files (list) : the list of directories for all files in the root folder
    -    """
    -    # create a list of file and sub directories
    -    # names in the given directory
    -    files_list = os.listdir(dir_name)
    -    all_files = list()
    -    # Iterate over all the entries
    -    for entry in files_list:
    -        # Create full path
    -        full_path = os.path.join(dir_name, entry)
    -        # If entry is a directory then get the list of files in this directory
    -        if os.path.isdir(full_path):
    -            all_files = all_files + get_list_files(full_path)
    -        else:
    -            path_split = os.path.splitext(full_path)
    -            if path_split[1] in [".jpg", ".jpeg", ".png"]:
    -                all_files.append(full_path)
    -
    -    return all_files
    -
    -
    -
    -def resize(path, pathnew, width=1024) -
    -
    -

    resize an image

    -

    Arguments

    -

    path (string) : the path of the image will be resized -pathnew (string) : the path of new resized image -Keyword Arguments: -width (int) : the width of the new image , hieght is depending on it

    -

    Returns

    -

    (string) : True if image resized successfully or the exception message if not

    -
    -Source code -
    def resize(path, pathnew, width=1024):
    -    """resize an image 
    -    
    -    Arguments:
    -        path (string) : the path of the image will be resized
    -        pathnew (string) : the path of new resized image
    -    
    -    Keyword Arguments:
    -        width (int) : the width of the new image , hieght is depending on it
    -    
    -    Returns:
    -        (string) : True if image resized successfully or the exception message if not
    -    """
    -    im = get_image(path)
    -    xnew = width
    -    x, y = im.size
    -    ynew = int(float(y) / (float(x) / float(xnew)))
    -    imnew = im.resize((xnew, ynew), Image.ANTIALIAS)
    -    try:
    -        imnew.save(pathnew)
    -        return "True"
    -    except Exception as e:
    -        return str(e)
    -
    -
    -
    -def resize_images_in_dir(folder, foldernew, width=1024) -
    -
    -

    resize images in folder

    -

    Arguments

    -

    folder (string) : the path of the root folder in which images will be resized -foldernew (string) : the path of the folder in which new images will be stored -Keyword Arguments: -width (int) : the width of the new images , hieght is depending on it

    -

    Returns

    -

    (dict) : a dict with img path as key and its statue as value -statue : True if image resized successfully or the exception message if not

    -
    -Source code -
    def resize_images_in_dir(folder, foldernew, width=1024):
    -    """resize images in folder
    -    
    -    Arguments:
    -        folder (string) : the path of the root folder in which images will be resized
    -        foldernew (string) : the path of the folder in which new images will be stored
    -    
    -    Keyword Arguments:
    -        width (int) : the width of the new images , hieght is depending on it
    -    
    -    Returns:
    -        (dict) : a dict with img path as key and its statue as value
    -            statue : True if image resized successfully or the exception message if not
    -    """
    -    img_list = get_list_files(folder)
    -    print(img_list)
    -    img_statue = {}
    -    for img in img_list:
    -        statue = resize(img, os.path.join(foldernew, os.path.basename(img)), width=width)
    -        img_statue[img] = statue
    -    return img_statue
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/tools/imagelib/index.html b/docs/api/jumpscale/tools/imagelib/index.html deleted file mode 100644 index 582b6d04f..000000000 --- a/docs/api/jumpscale/tools/imagelib/index.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - -jumpscale.tools.imagelib API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.imagelib

    -
    -
    -

    Wraps imagelib

    -

    TODO: examples

    -
    -Source code -
    """Wraps imagelib
    -
    -# TODO: examples
    -
    -"""
    -
    -from .imagelib import *
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.tools.imagelib.imagelib
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/tools/poolexecutor/index.html b/docs/api/jumpscale/tools/poolexecutor/index.html deleted file mode 100644 index 4221f0052..000000000 --- a/docs/api/jumpscale/tools/poolexecutor/index.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - -jumpscale.tools.poolexecutor API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.poolexecutor

    -
    -
    -
    -Source code -
    from .poolexecutor import PoolExecutor
    -
    -"""
    -
    -def sleepf(howlong, name="fun"):
    -    print("{} is sleeping for {}".format(name, howlong))
    -    for i in range(howlong):
    -        print("{} is sleeping slept for {}".format(name, howlong - i))
    -        gevent.sleep(i)
    -
    -with j.tools.poolexecutor.PoolExecutor() as p:
    -    for i in range(5):
    -        p.task_add(sleepf, i, name="fun{}".format(i))
    -
    -    gs = p.run()
    -    print(p.results(gs))
    -
    -
    -"""
    -
    -
    -
    -

    Sub-modules

    -
    -
    jumpscale.tools.poolexecutor.poolexecutor
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/tools/poolexecutor/poolexecutor.html b/docs/api/jumpscale/tools/poolexecutor/poolexecutor.html deleted file mode 100644 index 78219e7eb..000000000 --- a/docs/api/jumpscale/tools/poolexecutor/poolexecutor.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - -jumpscale.tools.poolexecutor.poolexecutor API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.poolexecutor.poolexecutor

    -
    -
    -
    -Source code -
    import gevent
    -
    -
    -class Job:
    -    def __init__(self, fun, *args, **kwargs):
    -        self.fun = fun
    -        self.args = args
    -        self.kwargs = kwargs
    -
    -
    -class PoolExecutor:
    -    def __init__(self):
    -        self.jobs = []
    -    
    -    def __enter__(self):
    -        return self
    -
    -    def __exit__(self, type, value, tb):
    -        pass
    -
    -    def task_add(self, fun, *args, **kwargs):
    -        self.jobs.append(Job(fun, *args, **kwargs))
    -
    -    def run(self, die=True):
    -        try:
    -            greenlets = [gevent.spawn(job.fun, *job.args, **job.kwargs) for job in self.jobs]
    -            # print("greenlets: ", greenlets)
    -            gevent.joinall(greenlets, raise_error=die)
    -        except Exception as e:
    -            self.jobs = []
    -            raise e
    -        else:
    -            self.jobs = []
    -            return greenlets
    -    
    -    def results(self, greenlets):
    -        return [greenlet.value for greenlet in greenlets]
    -
    -
    -    # def test_simple(self):
    -    #     with j.tools.poolexecutor.PoolExecutor() as p:
    -    #         for i in range(5):
    -    #             p.task_add(sleepf, i, name="fun{}".format(i))
    -
    -    #         gs = p.run()
    -    #         p.results(gs)
    -    
    -    #     def sleepf(howlong, name="fun"):
    -    #         print("{} is sleeping for {}".format(name, howlong))
    -    #         for i in range(howlong):
    -    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    -    #             gevent.sleep(i)
    -
    -    #     for i in range(5):
    -    #         self.task_add(sleepf, i, name="fun{}".format(i))
    -
    -    #     self.run()
    -
    -    # def test_with_errors(self):
    -
    -    #     def sleepf(howlong, name="fun"):
    -    #         print("{} is sleeping for {}".format(name, howlong))
    -    #         for i in range(howlong):
    -    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    -    #             gevent.sleep(i)
    -
    -    #     def sleepf_with_error(howlong, name="fun"):
    -    #         print("{} is sleeping for {}".format(name, howlong))
    -    #         for i in range(howlong):
    -    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    -    #             gevent.sleep(i)
    -    #         raise RuntimeError("error here in sleepf_with_error")
    -
    -    #     for i in range(5):
    -    #         self.task_add(sleepf, i, name="fun{}".format(i))
    -
    -    #     self.task_add(sleepf_with_error, i, name="error_fun")
    -
    -    #     try:
    -    #         self.run()
    -    #     except:
    -    #         print("run has a function that raises and we caught it.")
    -
    -    # def test_with_results(self):
    -
    -    #     def sleepf(howlong, name="fun"):
    -    #         print("{} is sleeping for {}".format(name, howlong))
    -    #         for i in range(howlong):
    -    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    -    #             gevent.sleep(i)
    -    #         return 7
    -
    -    #     for i in range(5):
    -    #         self.task_add(sleepf, i, name="fun{}".format(i))
    -
    -    #     greenlets = self.run()
    -    #     results = [greenlet.value for greenlet in greenlets]
    -    #     assert all(map(lambda x: x == 7, results)) == True
    -    #     print(results)
    -
    -    # def test(self):
    -    #     for f in [self.test_simple, self.test_with_results, self.test_with_errors]:
    -    #         f()
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Classes

    -
    -
    -class Job -(fun, *args, **kwargs) -
    -
    -
    -
    -Source code -
    class Job:
    -    def __init__(self, fun, *args, **kwargs):
    -        self.fun = fun
    -        self.args = args
    -        self.kwargs = kwargs
    -
    -
    -
    -class PoolExecutor -
    -
    -
    -
    -Source code -
    class PoolExecutor:
    -    def __init__(self):
    -        self.jobs = []
    -    
    -    def __enter__(self):
    -        return self
    -
    -    def __exit__(self, type, value, tb):
    -        pass
    -
    -    def task_add(self, fun, *args, **kwargs):
    -        self.jobs.append(Job(fun, *args, **kwargs))
    -
    -    def run(self, die=True):
    -        try:
    -            greenlets = [gevent.spawn(job.fun, *job.args, **job.kwargs) for job in self.jobs]
    -            # print("greenlets: ", greenlets)
    -            gevent.joinall(greenlets, raise_error=die)
    -        except Exception as e:
    -            self.jobs = []
    -            raise e
    -        else:
    -            self.jobs = []
    -            return greenlets
    -    
    -    def results(self, greenlets):
    -        return [greenlet.value for greenlet in greenlets]
    -
    -

    Methods

    -
    -
    -def results(self, greenlets) -
    -
    -
    -
    -Source code -
    def results(self, greenlets):
    -    return [greenlet.value for greenlet in greenlets]
    -
    -
    -
    -def run(self, die=True) -
    -
    -
    -
    -Source code -
    def run(self, die=True):
    -    try:
    -        greenlets = [gevent.spawn(job.fun, *job.args, **job.kwargs) for job in self.jobs]
    -        # print("greenlets: ", greenlets)
    -        gevent.joinall(greenlets, raise_error=die)
    -    except Exception as e:
    -        self.jobs = []
    -        raise e
    -    else:
    -        self.jobs = []
    -        return greenlets
    -
    -
    -
    -def task_add(self, fun, *args, **kwargs) -
    -
    -
    -
    -Source code -
    def task_add(self, fun, *args, **kwargs):
    -    self.jobs.append(Job(fun, *args, **kwargs))
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - \ No newline at end of file diff --git a/docs/api/jumpscale/tools/profiler/index.html b/docs/api/jumpscale/tools/profiler/index.html deleted file mode 100644 index 45ba0608d..000000000 --- a/docs/api/jumpscale/tools/profiler/index.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - - -jumpscale.tools.profiler API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.profiler

    -
    -
    -

    This module is used to do profiling for methods. profiling can be visulaized or just printed to stdout -How to use it

    -
    @j.tools.profiler.profiled(visualized=True)
    -def foo():
    -  for i in range(10):
    -    print("test")
    -
    -

    to do visualizing, add (visualize=True) when u call profiled decorator -example -@j.tools.profiler.profiled() # this will print the profiling results to stdout -j.tools.profiler.profiled(visualized=True) # will launce a server with the visualized profiling on <http://127.0.0.1:8080/snakeviz/%2Fsandbox%2Fcode%2Fgithub%2Fthreefoldtech%2Fjs-ng%2Fresult.prof> -to change port and host -j.tools.profiler.profiled(visualized=True, port="8008", host="0.0.0.0", print_data=True) -this will print data to stdout and launce snakeviz server on this url -<http://127.0.0.1:8080/snakeviz/foo>

    -
    -Source code -
    """This module is used to do profiling for methods. profiling can be visulaized or just printed to stdout
    -How to use it
    -```
    -@j.tools.profiler.profiled(visualized=True)
    -def foo():
    -  for i in range(10):
    -    print("test")
    -```
    -to do visualizing, add (visualize=True) when u call profiled decorator
    -example
    -@j.tools.profiler.profiled() # this will print the profiling results to stdout
    -j.tools.profiler.profiled(visualized=True) # will launce a server with the visualized profiling on `http://127.0.0.1:8080/snakeviz/%2Fsandbox%2Fcode%2Fgithub%2Fthreefoldtech%2Fjs-ng%2Fresult.prof`
    -to change port and host
    -j.tools.profiler.profiled(visualized=True, port="8008", host="0.0.0.0", print_data=True)
    -this will print data to stdout and launce snakeviz server on this url
    -`http://127.0.0.1:8080/snakeviz/foo`
    -"""
    -from cProfile import Profile
    -from pstats import Stats
    -from snakeviz.main import app
    -import tornado
    -from subprocess import Popen
    -import socket
    -
    -
    -def profiled(visualized=False, host="127.0.0.1", port="8080", print_data=False):
    -    def do_profiling(func):
    -        def wrapper(*args, **kwargs):
    -            profiler = Profile()
    -            result = profiler.runcall(func, *args, **kwargs)
    -            if print_data:
    -                profiler.print_stats()
    -            filename = func.__name__
    -            profiler.dump_stats(filename)
    -            if visualized:
    -                visualize(filename, host, port)
    -            return result
    -
    -        return wrapper
    -
    -    return do_profiling
    -
    -
    -def visualize(filename, host="127.0.0.1", port="8080"):
    -    try:
    -        Stats(filename)
    -    except Exception as e:
    -        print(f"{filename} is not a valid stats file")
    -        raise e
    -    try:
    -        conn = app.listen(port, address=host)
    -    except socket.error as e:
    -        print("Port {0} already in use.".format(port))
    -        raise e
    -
    -    url = "http://{0}:{1}/snakeviz/{2}".format(host, port, filename)
    -    print(f"snakeviz web server started on {host}:{port}; enter Ctrl-C to exit")
    -    print(url)
    -
    -    try:
    -        tornado.ioloop.IOLoop.instance().start()
    -    except KeyboardInterrupt:
    -        conn.stop()
    -        print("\nBye!")
    -
    -    return 0
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def profiled(visualized=False, host='127.0.0.1', port='8080', print_data=False) -
    -
    -
    -
    -Source code -
    def profiled(visualized=False, host="127.0.0.1", port="8080", print_data=False):
    -    def do_profiling(func):
    -        def wrapper(*args, **kwargs):
    -            profiler = Profile()
    -            result = profiler.runcall(func, *args, **kwargs)
    -            if print_data:
    -                profiler.print_stats()
    -            filename = func.__name__
    -            profiler.dump_stats(filename)
    -            if visualized:
    -                visualize(filename, host, port)
    -            return result
    -
    -        return wrapper
    -
    -    return do_profiling
    -
    -
    -
    -def visualize(filename, host='127.0.0.1', port='8080') -
    -
    -
    -
    -Source code -
    def visualize(filename, host="127.0.0.1", port="8080"):
    -    try:
    -        Stats(filename)
    -    except Exception as e:
    -        print(f"{filename} is not a valid stats file")
    -        raise e
    -    try:
    -        conn = app.listen(port, address=host)
    -    except socket.error as e:
    -        print("Port {0} already in use.".format(port))
    -        raise e
    -
    -    url = "http://{0}:{1}/snakeviz/{2}".format(host, port, filename)
    -    print(f"snakeviz web server started on {host}:{port}; enter Ctrl-C to exit")
    -    print(url)
    -
    -    try:
    -        tornado.ioloop.IOLoop.instance().start()
    -    except KeyboardInterrupt:
    -        conn.stop()
    -        print("\nBye!")
    -
    -    return 0
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/tools/tfgateway/index.html b/docs/api/jumpscale/tools/tfgateway/index.html deleted file mode 100644 index 972cf730c..000000000 --- a/docs/api/jumpscale/tools/tfgateway/index.html +++ /dev/null @@ -1,409 +0,0 @@ - - - - - - -jumpscale.tools.tfgateway API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.tfgateway

    -
    -
    -
    -Source code -
    from jumpscale.loader import j
    -
    -from ipaddress import IPv4Address, IPv6Address
    -
    -# TODO: fixme when ipaddr is primitive in types.
    -def addr_check(addr):
    -    try:
    -        IPv4Address(addr)
    -    except:
    -        try:
    -            IPv6Address(addr)
    -        except:
    -            return False
    -        else:
    -            return True
    -    else:
    -        return True
    -
    -"""
    -j.tools.tf_gateway.tcpservice_register("bing", "www.bing.com", "122.124.214.21")
    -j.tools.tf_gateway.domain_register_a("ahmed", "bots.grid.tf.", "123.3.23.54")
    -
    -"""
    -
    -def local_redis():
    -    local = None
    -    try:
    -        local = j.clients.redis.get('local')
    -    except:
    -        local = j.clients.redis.new('local')
    -
    -    return local
    -
    -def tcpservice_register(service_name, domain, service_endpoint):
    -    """
    -    register a tcpservice to be used by tcprouter in local_redis()
    -
    -    :param service_name: service name to register in tcprouter
    -    :type service_name: str
    -    :param domain: (Server Name Indicator SNI) (e.g www.facebook.com)
    -    :type domain: str
    -    :param service_endpoint: TLS endpoint 102.142.96.34:443 "ip:port"
    -    :type service_endpoint: string
    -    """
    -    service = {}
    -    service["Key"] = "/tcprouter/service/{}".format(service_name)
    -    record = {"addr": service_endpoint, "sni": domain, "name": service_name}
    -    json_dumped_record_bytes = j.data.serializers.json.dumps(record).encode()
    -    b64_record = j.data.serializers.base64.encode(json_dumped_record_bytes).decode()
    -    service["Value"] = b64_record
    -    local_redis().set(service["Key"], j.data.serializers.json.dumps(service))
    -
    -def domain_register(threebot_name, bots_domain="bots.grid.tf.", record_type="a", records=None):
    -    """registers domain in coredns (needs to be authoritative)
    -
    -    e.g: ahmed.bots.grid.tf
    -
    -    requires nameserver on bots.grid.tf (authoritative)
    -    - ahmed is threebot_name
    -    - bots_domain is bots.grid.tf
    -
    -    :param threebot_name: threebot_name
    -    :type threebot_name: str
    -    :param bots_domain: str, defaults to "bots.grid.tf."
    -    :type bots_domain: str, optional
    -    :param record_type: valid dns record (a, aaaa, txt, srv..), defaults to "a"
    -    :type record_type: str, optional
    -    :param records: records list, defaults to None
    -    :type records: [type], optional is [ {"ip":machine ip}] in case of a/aaaa records
    -    """
    -    if not bots_domain.endswith("."):
    -        bots_domain += "."
    -    data = {}
    -    records = records or []
    -    if local_redis().hexists(bots_domain, threebot_name):
    -        data = j.data.serializers.json.loads(local_redis().hget(bots_domain, threebot_name))
    -
    -    if record_type in data:
    -        records.extend(data[record_type])
    -    data[record_type] = records
    -    local_redis().hset(bots_domain, threebot_name, j.data.serializers.json.dumps(data))
    -
    -def domain_register_a(name, domain, record_ip):
    -    """registers A domain in coredns (needs to be authoritative)
    -
    -    e.g: ahmed.bots.grid.tf
    -
    -    requires nameserver on bots.grid.tf (authoritative)
    -    - ahmed is threebot_name
    -    - bots_domain is bots.grid.tf
    -
    -    :param threebot_name: myhost
    -    :type threebot_name: str
    -    :param bots_domain: str, defaults to "grid.tf."
    -    :type bots_domain: str, optional
    -    :param record_ip: machine ip in ipv4 format
    -    :type record_ip: str
    -    """
    -    if addr_check(record_ip):
    -        return domain_register(name, domain, record_type="a", records=[{"ip": record_ip}])
    -    else:
    -        raise j.exceptions.Value("invalid ip {record_ip}".format(**locals()))
    -
    -def domain_register_aaaa(threebot_name, bots_domain, record_ip):
    -    """registers A domain in coredns (needs to be authoritative)
    -
    -    e.g: ahmed.bots.grid.tf
    -
    -    requires nameserver on bots.grid.tf (authoritative)
    -    - ahmed is threebot_name
    -    - bots_domain is bots.grid.tf
    -
    -    :param threebot_name: threebot_name
    -    :type threebot_name: str
    -    :param bots_domain: str, defaults to "bots.grid.tf."
    -    :type bots_domain: str, optional
    -    :param record_ip: machine ip in ipv6 format
    -    :type record_ip: str
    -    """
    -    if addr_check(record_ip):
    -        return domain_register(threebot_name, bots_domain, record_type="aaaa", records=[{"ip": record_ip}])
    -    else:
    -        raise j.exceptions.Value("invalid ip {record_ip}".format(**locals()))
    -
    -
    -def test():
    -    domain_register_a("ns", "3bot.me", "134.209.90.92")
    -    domain_register_a("a", "3bot.me", "134.209.90.92")
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def addr_check(addr) -
    -
    -
    -
    -Source code -
    def addr_check(addr):
    -    try:
    -        IPv4Address(addr)
    -    except:
    -        try:
    -            IPv6Address(addr)
    -        except:
    -            return False
    -        else:
    -            return True
    -    else:
    -        return True
    -
    -
    -
    -def domain_register(threebot_name, bots_domain='bots.grid.tf.', record_type='a', records=None) -
    -
    -

    registers domain in coredns (needs to be authoritative)

    -

    e.g: ahmed.bots.grid.tf

    -

    requires nameserver on bots.grid.tf (authoritative) -- ahmed is threebot_name -- bots_domain is bots.grid.tf

    -

    :param threebot_name: threebot_name -:type threebot_name: str -:param bots_domain: str, defaults to "bots.grid.tf." -:type bots_domain: str, optional -:param record_type: valid dns record (a, aaaa, txt, srv..), defaults to "a" -:type record_type: str, optional -:param records: records list, defaults to None -:type records: [type], optional is [ {"ip":machine ip}] in case of a/aaaa records

    -
    -Source code -
    def domain_register(threebot_name, bots_domain="bots.grid.tf.", record_type="a", records=None):
    -    """registers domain in coredns (needs to be authoritative)
    -
    -    e.g: ahmed.bots.grid.tf
    -
    -    requires nameserver on bots.grid.tf (authoritative)
    -    - ahmed is threebot_name
    -    - bots_domain is bots.grid.tf
    -
    -    :param threebot_name: threebot_name
    -    :type threebot_name: str
    -    :param bots_domain: str, defaults to "bots.grid.tf."
    -    :type bots_domain: str, optional
    -    :param record_type: valid dns record (a, aaaa, txt, srv..), defaults to "a"
    -    :type record_type: str, optional
    -    :param records: records list, defaults to None
    -    :type records: [type], optional is [ {"ip":machine ip}] in case of a/aaaa records
    -    """
    -    if not bots_domain.endswith("."):
    -        bots_domain += "."
    -    data = {}
    -    records = records or []
    -    if local_redis().hexists(bots_domain, threebot_name):
    -        data = j.data.serializers.json.loads(local_redis().hget(bots_domain, threebot_name))
    -
    -    if record_type in data:
    -        records.extend(data[record_type])
    -    data[record_type] = records
    -    local_redis().hset(bots_domain, threebot_name, j.data.serializers.json.dumps(data))
    -
    -
    -
    -def domain_register_a(name, domain, record_ip) -
    -
    -

    registers A domain in coredns (needs to be authoritative)

    -

    e.g: ahmed.bots.grid.tf

    -

    requires nameserver on bots.grid.tf (authoritative) -- ahmed is threebot_name -- bots_domain is bots.grid.tf

    -

    :param threebot_name: myhost -:type threebot_name: str -:param bots_domain: str, defaults to "grid.tf." -:type bots_domain: str, optional -:param record_ip: machine ip in ipv4 format -:type record_ip: str

    -
    -Source code -
    def domain_register_a(name, domain, record_ip):
    -    """registers A domain in coredns (needs to be authoritative)
    -
    -    e.g: ahmed.bots.grid.tf
    -
    -    requires nameserver on bots.grid.tf (authoritative)
    -    - ahmed is threebot_name
    -    - bots_domain is bots.grid.tf
    -
    -    :param threebot_name: myhost
    -    :type threebot_name: str
    -    :param bots_domain: str, defaults to "grid.tf."
    -    :type bots_domain: str, optional
    -    :param record_ip: machine ip in ipv4 format
    -    :type record_ip: str
    -    """
    -    if addr_check(record_ip):
    -        return domain_register(name, domain, record_type="a", records=[{"ip": record_ip}])
    -    else:
    -        raise j.exceptions.Value("invalid ip {record_ip}".format(**locals()))
    -
    -
    -
    -def domain_register_aaaa(threebot_name, bots_domain, record_ip) -
    -
    -

    registers A domain in coredns (needs to be authoritative)

    -

    e.g: ahmed.bots.grid.tf

    -

    requires nameserver on bots.grid.tf (authoritative) -- ahmed is threebot_name -- bots_domain is bots.grid.tf

    -

    :param threebot_name: threebot_name -:type threebot_name: str -:param bots_domain: str, defaults to "bots.grid.tf." -:type bots_domain: str, optional -:param record_ip: machine ip in ipv6 format -:type record_ip: str

    -
    -Source code -
    def domain_register_aaaa(threebot_name, bots_domain, record_ip):
    -    """registers A domain in coredns (needs to be authoritative)
    -
    -    e.g: ahmed.bots.grid.tf
    -
    -    requires nameserver on bots.grid.tf (authoritative)
    -    - ahmed is threebot_name
    -    - bots_domain is bots.grid.tf
    -
    -    :param threebot_name: threebot_name
    -    :type threebot_name: str
    -    :param bots_domain: str, defaults to "bots.grid.tf."
    -    :type bots_domain: str, optional
    -    :param record_ip: machine ip in ipv6 format
    -    :type record_ip: str
    -    """
    -    if addr_check(record_ip):
    -        return domain_register(threebot_name, bots_domain, record_type="aaaa", records=[{"ip": record_ip}])
    -    else:
    -        raise j.exceptions.Value("invalid ip {record_ip}".format(**locals()))
    -
    -
    -
    -def local_redis() -
    -
    -
    -
    -Source code -
    def local_redis():
    -    local = None
    -    try:
    -        local = j.clients.redis.get('local')
    -    except:
    -        local = j.clients.redis.new('local')
    -
    -    return local
    -
    -
    -
    -def tcpservice_register(service_name, domain, service_endpoint) -
    -
    -

    register a tcpservice to be used by tcprouter in local_redis()

    -

    :param service_name: service name to register in tcprouter -:type service_name: str -:param domain: (Server Name Indicator SNI) (e.g www.facebook.com) -:type domain: str -:param service_endpoint: TLS endpoint 102.142.96.34:443 "ip:port" -:type service_endpoint: string

    -
    -Source code -
    def tcpservice_register(service_name, domain, service_endpoint):
    -    """
    -    register a tcpservice to be used by tcprouter in local_redis()
    -
    -    :param service_name: service name to register in tcprouter
    -    :type service_name: str
    -    :param domain: (Server Name Indicator SNI) (e.g www.facebook.com)
    -    :type domain: str
    -    :param service_endpoint: TLS endpoint 102.142.96.34:443 "ip:port"
    -    :type service_endpoint: string
    -    """
    -    service = {}
    -    service["Key"] = "/tcprouter/service/{}".format(service_name)
    -    record = {"addr": service_endpoint, "sni": domain, "name": service_name}
    -    json_dumped_record_bytes = j.data.serializers.json.dumps(record).encode()
    -    b64_record = j.data.serializers.base64.encode(json_dumped_record_bytes).decode()
    -    service["Value"] = b64_record
    -    local_redis().set(service["Key"], j.data.serializers.json.dumps(service))
    -
    -
    -
    -def test() -
    -
    -
    -
    -Source code -
    def test():
    -    domain_register_a("ns", "3bot.me", "134.209.90.92")
    -    domain_register_a("a", "3bot.me", "134.209.90.92")
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/api/jumpscale/tools/timer/timer.html b/docs/api/jumpscale/tools/timer/timer.html deleted file mode 100644 index e3a3840be..000000000 --- a/docs/api/jumpscale/tools/timer/timer.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -jumpscale.tools.timer.timer API documentation - - - - - - - - - -
    -
    -
    -

    Module jumpscale.tools.timer.timer

    -
    -
    -
    -Source code -
    import time
    -from jumpscale.loader import j
    -
    -
    -def timeit(func):
    -    def wrapper(*args, **kwargs):
    -        start_time = time.time()
    -        result = func(*args, **kwargs)
    -        end_time = time.time()
    -        diff = end_time - start_time
    -        j.logger.info("func {} with args: {}, kwargs: {} took {}".format(func.__name__, args, kwargs, diff))
    -        return result
    -
    -    return wrapper
    -
    -
    -
    -
    -
    -
    -
    -

    Functions

    -
    -
    -def timeit(func) -
    -
    -
    -
    -Source code -
    def timeit(func):
    -    def wrapper(*args, **kwargs):
    -        start_time = time.time()
    -        result = func(*args, **kwargs)
    -        end_time = time.time()
    -        diff = end_time - start_time
    -        j.logger.info("func {} with args: {}, kwargs: {} took {}".format(func.__name__, args, kwargs, diff))
    -        return result
    -
    -    return wrapper
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - diff --git a/docs/wiki/3sdk.md b/docs/wiki/3sdk.md deleted file mode 100644 index 0ce851609..000000000 --- a/docs/wiki/3sdk.md +++ /dev/null @@ -1,69 +0,0 @@ -# Working with 3sdk - -`3sdk` is a command line tool that provides the user with a simple interface to orchestrate his threebot containers. -It allows the user to create a container that is ready to start a threebot server on with the user identity already registered on that container. - -## Using 3sdk - -You can get the binary from the [release page](https://github.com/threefoldtech/js-ng/releases). Make sure to change permission to allow the binary to be executed as follows: - -```bash -chmod +x {binary_path} -``` - -Once inside the shell you can see that it provides auto completion to the available commands: - -![Shell auto completion](images/sdkautocomplete.png) - -To install a new threebot container: - -![SDK install](images/sdkinstall.png) - -The user will be prompted to enter his user information which includes: - -- His 3bot name -- His 3bot email -- His phrase which is normally found in his `3bot connect` app - -![SDK prompt](images/sdkprompt.png) - -The user is then asked to choose which explorer networks he is in: - -![SDK explorer](images/sdkexplorer.png) - -The container should be created now with default name `3bot-ng`. - -## Building the 3sdk - -You can get latest available binary by using `pyinstaller`. Which creates the `3sdk` binary based on `threesdk.spec` file. - -To install `pyinstaller` run: - -```bash -pip3 install pyinstaller -``` - -And then to create the binary: - -```bash -cd {jsng repo path} -pyinstaller threesdk.spec -``` - -Binary should be created under `dist` directory in the repo root, and is ready to be used. - -### Building a static executable - -You bundle the dynamic executables to ensure that the binary can be run on any system by creating a static executable. - -First you need to install `staticx`: - -```bash -pip3 install staticx -``` - -After building your binary run the following for a static executable: - -```bash -staticx dist/3sdk 3sdk.static -``` diff --git a/docs/wiki/README.md b/docs/wiki/README.md index 7cec31ec5..6c6baf08b 100644 --- a/docs/wiki/README.md +++ b/docs/wiki/README.md @@ -30,7 +30,7 @@ * [Extending Jumpscale](./extend_j.md) * [Adding a new command](./distributing_cmds.md) * [Executors](./executors.md) - * [Registering new extensions on God object](./extend_j.md) + * [Registering new extensions on loader object](./extend_j.md) * [Logging](./logging.md) * [Exceptions](./exceptions.md) * [Errors](./errorhandling.md) @@ -41,7 +41,7 @@ * [Github actions](./githubactions.md) * [Specs](specs.md) * [Poetry](poetry.md) - * [God object and namespaces](god_object_namespaces_concepts.md) + * [Loader object and namespaces](loader_object_namespaces_concepts.md) * [Loader](loader.md) * [JS-NG shell](jsng_shell.md) * [Base Classes and Hierarchical Configurations](baseclasses.md) @@ -49,4 +49,4 @@ * [Exceptions](exceptions.md) * [Logging](logging.md) * [Executors](executors.md) - * [Distributing Commands](distributing_cmds.md) \ No newline at end of file + * [Distributing Commands](distributing_cmds.md) diff --git a/docs/wiki/_sidebar.md b/docs/wiki/_sidebar.md index b48d731d1..664092325 100644 --- a/docs/wiki/_sidebar.md +++ b/docs/wiki/_sidebar.md @@ -33,7 +33,7 @@ * [Extending Jumpscale](./extend_j.md) * [Adding a new command](./distributing_cmds.md) * [Executors](./executors.md) - * [Registering new extensions on God object](./extend_j.md) + * [Registering new extensions on loader object](./extend_j.md) * [Logging](./logging.md) * [Exceptions](./exceptions.md) * [Errors](./errorhandling.md) @@ -48,7 +48,7 @@ * [Specs](specs.md) * [Poetry](poetry.md) - * [God object and namespaces](god_object_namespaces_concepts.md) + * [Loader object and namespaces](loader_object_namespaces_concepts.md) * [Loader](loader.md) * [JS-NG shell](jsng_shell.md) * [Base Classes and Hierarchical Configurations](baseclasses.md) diff --git a/docs/wiki/docker.md b/docs/wiki/docker.md deleted file mode 100644 index 639354653..000000000 --- a/docs/wiki/docker.md +++ /dev/null @@ -1,16 +0,0 @@ -# Starting docker container with js-ng installed -## Start usershell with this -``` - poetry run usershell -``` -## instantiate docker container from js-ng usershell -``` -container.install(name="jsng", image="threefoldtech/js-ng:latest", ports=None, volumes=None, devices=None, identity=None) -# name is the docker container name, default is jsng -# image is the jsng-image you want to run, default is threefoldtech/js-ng:latest -# ports is the port you want to forward -# volumes is the volumes you want to mount on docker -# devices the devices you want to include in docker -# identity is the private key you want to create on docker -# mount_code mount codedir into container,default is True -``` diff --git a/docs/wiki/god_object_namespaces_concepts.md b/docs/wiki/loader_object_namespaces_concepts.md similarity index 97% rename from docs/wiki/god_object_namespaces_concepts.md rename to docs/wiki/loader_object_namespaces_concepts.md index d4fe7a8cd..81c1d952c 100644 --- a/docs/wiki/god_object_namespaces_concepts.md +++ b/docs/wiki/loader_object_namespaces_concepts.md @@ -66,7 +66,7 @@ Example structure ├── projectmain │ └── jumpscale -│ ├── god.py +│ ├── loader.py │ ├── __init__.py ├── projectsals │ └── jumpscale @@ -104,7 +104,7 @@ Solved by design using python modules # Problem 4: jslocation -If we open `god.py` +If we do like the following ```python import jumpscale.sal @@ -113,9 +113,10 @@ import jumpscale.clients j = jumpscale ``` + we have handcrafted imports for sal, tools, clients so their subpackages can be autoloaded, but how should it work with packages like `digitalme` -## How to register digitalme in the god object +## How to register digitalme in the global object "j" Do we generate `import jumpscale.digitalme`? is there a standard python way to do it? a reliable plugin system? ## where would its module be registered? diff --git a/docs/wiki/specs.md b/docs/wiki/specs.md index 6b1882fc9..4dab58c5e 100644 --- a/docs/wiki/specs.md +++ b/docs/wiki/specs.md @@ -1,5 +1,5 @@ * [Poetry](poetry.md) -* [God object and namespaces](god_object_namespaces_concepts.md) +* [Loader object and namespaces](loader_object_namespaces_concepts.md) * [Loader](loader.md) * [JS-NG shell](jsng_shell.md) * [Base Classes and Hierarchical Configurations](baseclasses.md) @@ -7,4 +7,4 @@ * [Exceptions](exceptions.md) * [Logging](logging.md) * [Executors](executors.md) -* [Distributing Commands](distributing_cmds.md) \ No newline at end of file +* [Distributing Commands](distributing_cmds.md) diff --git a/docs/wiki/tutorials/developing_sal.md b/docs/wiki/tutorials/developing_sal.md index 18e6b4bf3..8739c4a93 100644 --- a/docs/wiki/tutorials/developing_sal.md +++ b/docs/wiki/tutorials/developing_sal.md @@ -26,6 +26,4 @@ You can put all of your code directly in the `__init__.py` of your SAL package. ## Accessing code -Code will be auto registered in the god object `j` like that `j.sal.YOUR_NEW_SAL` - - +Code will be auto registered in the loader object `j` like that `j.sal.YOUR_NEW_SAL` diff --git a/jumpscale/core/config/config.py b/jumpscale/core/config/config.py index 421250dc1..b90846b86 100644 --- a/jumpscale/core/config/config.py +++ b/jumpscale/core/config/config.py @@ -114,7 +114,10 @@ def get_default_config(): "debug": True, "shell": "ptpython", "logging": { - "default": {"enabled": True, "level": 10,}, + "default": { + "enabled": True, + "level": 10, + }, "redis": { "enabled": True, "level": 15, @@ -139,8 +142,9 @@ def get_default_config(): }, "factory": {"always_reload": False}, "store": "filesystem", - "threebot": {"default": "",}, - "explorer": {"default_url": "https://explorer.testnet.grid.tf/explorer",}, + "threebot": { + "default": "", + }, } @@ -165,7 +169,7 @@ def update_config(data): def get(key, default=None): - """ Retrives value from jumpscale config + """Retrives value from jumpscale config Arguments: key (str): the key you wish to retrieve @@ -176,7 +180,7 @@ def get(key, default=None): def set(key, val): - """ Sets value in jumpscale config + """Sets value in jumpscale config Arguments: key (str): the key you wish to update @@ -188,7 +192,7 @@ def set(key, val): def set_default(key, val): - """ Sets key to value in jumpscale config and returns + """Sets key to value in jumpscale config and returns Arguments: key (str): the key you wish to update diff --git a/jumpscale/entry_points/usershell.py b/jumpscale/entry_points/usershell.py deleted file mode 100644 index 9e5cc9600..000000000 --- a/jumpscale/entry_points/usershell.py +++ /dev/null @@ -1,247 +0,0 @@ -import os -import re -import time -import sys -import traceback -import argparse -import requests - -import inspect -import cgi - -from prompt_toolkit import PromptSession -from prompt_toolkit.completion import Completion -from prompt_toolkit.shortcuts import print_formatted_text -from prompt_toolkit.eventloop.async_generator import AsyncGeneratorItem -from prompt_toolkit.validation import Validator, ValidationError -from prompt_toolkit.styles import Style -from prompt_toolkit.formatted_text import HTML -from jumpscale import threesdk -from jumpscale.threesdk import settings -from jumpscale.core.exceptions.exceptions import JSException -from jumpscale.clients.docker.docker import DockerClient -from jumpscale.threesdk.threebot import ThreeBot, DEFAULT_IMAGE -from jumpscale.core.config import get_current_version - - -BASE_CONFIG_DIR = os.path.join(os.environ.get("HOME", "/root"), ".jsng") -HISTORY_FILENAME = os.path.join(BASE_CONFIG_DIR, "history.txt") - -DEFAULT_TOOLBAR_MSG = "Welcome to 3sdk enter info for help" - -style = Style.from_dict( - { - # User input (default text). - "bottom-toolbar": "#ffffff bg:#333333", - "default": "#aaaaaa", - # Prompt. - } -) - - -def get_binary_link(): - resp = requests.get("https://api.github.com/repos/threefoldtech/js-ng/releases/latest") - resp = resp.json() - # get versions - download_link = "" - version = resp["tag_name"] - for platform in resp["assets"]: - if sys.platform in platform["name"]: - download_link = platform["browser_download_url"] - return version, download_link - - -def update(): - print("checking for updates") - latest_version, binary_link = get_binary_link() - current_version = get_current_version() - if latest_version != current_version: - print(f"version: {latest_version} is available get it from {binary_link}") - return - docker_client = DockerClient() - print("Checking for new docker image") - docker_client.client.images.pull(f"{DEFAULT_IMAGE}:{latest_version}") - print("Starting 3sdk containers") - for container_name in os.listdir(os.path.expanduser("~/.config/jumpscale/containers")): - ThreeBot.delete(container_name) - ThreeBot.install(container_name) - - -def print_error(error): - print_formatted_text(HTML("{}".format(cgi.html.escape(str(error))))) - - -def partition_line(line): - def replacer(m): - return m.group().replace(" ", "\0").strip("\"'") - - result = re.sub(r"""(['"]).*?\1""", replacer, line) - parts = [] - for part in result.split(): - parts.append(part.replace("\0", " ")) - return parts - - -def noexpert_error(error): - reports_location = f"{os.environ.get('HOME', os.environ.get('USERPROFILE', ''))}/sandbox/reports" - error_file_location = f"{reports_location}/jsngreport_{time.strftime('%d%H%M%S')}.log" - if not os.path.exists(reports_location): - os.makedirs(reports_location) - with open(error_file_location, "w") as f: - f.write(str(error)) - err_msg = f"""Something went wrong. Please contact support at https://support.grid.tf/ -Error report file has been created on your machine in this location: {error_file_location} - """ - return err_msg - - -class Shell(Validator): - def __init__(self): - self._prompt = PromptSession() - self.mode = None - self.toolbarmsg = DEFAULT_TOOLBAR_MSG - - def get_completions_async(self, document, complete_event): - text = document.current_line_before_cursor - parts = partition_line(text) - if not parts: - root = None - more = [] - else: - root, more = parts[0], parts[1:] - items = [] - if not root or not hasattr(threesdk, root): - style = "bg:ansibrightblue" - items += threesdk.__all__ - self.toolbarmsg = DEFAULT_TOOLBAR_MSG - else: - style = "bg:ansigreen" - obj = getattr(threesdk, root) - if not more or not hasattr(obj, more[0]): - # complete object attributes - self.toolbarmsg = threesdk._get_doc_line(obj.__doc__) - for name, member in inspect.getmembers(obj, inspect.isroutine): - if not name.startswith("_"): - items.append(name) - text = "" if not more else more[-1] - else: - # complete arguments - func = getattr(obj, more[0]) - self.toolbarmsg = threesdk._get_doc_line(func.__doc__) - style = "bg:ansired" - for arg in inspect.getfullargspec(func).args: - field = arg + "=" - if field in text: - continue - items.append(field) - if len(more) > 1: - text = more[-1] - else: - text = "" - - for item in items: - if not item: - continue - if isinstance(item, Completion): - item.start_position = -len(text) - else: - item = Completion(item, -len(text)) - regex = ".*".join(text) - item.style = style - if not text or re.search(regex, item.text): - yield AsyncGeneratorItem(item) - - def bottom_toolbar(self): - return [("class:bottom-toolbar", self.toolbarmsg)] - - def validate(self, document): - text = document.current_line_before_cursor - if not text: - return - root, *more = text.split(" ") - submodule = getattr(threesdk, root, None) - if not submodule: - raise ValidationError(message=f"No such subcommand {root}") - if not more and callable(submodule): - func = root - elif more: - func = getattr(submodule, more[0], None) - if not func: - raise ValidationError(message=f"{root} has no command called {more[0]}") - else: - raise ValidationError(message="Invalid command") - # TODO: validate args - return - - def get_func_kwargs(self, cmd): - parts = partition_line(cmd) - root, extra = parts[0], parts[1:] - module = getattr(threesdk, root) - if inspect.isroutine(module): - return module, self.get_kwargs(module, *extra) - else: - func = getattr(module, extra[0]) - return func, self.get_kwargs(func, *extra[1:]) - - def get_kwargs(self, func, *args): - funcspec = inspect.getfullargspec(func) - kwargs = {} - for arg in args: - key, val = arg.split("=", 1) - isbool = funcspec.annotations.get(key) is bool - if isbool: - if val: - val = val.lower() in ["y", "yes", "1", "true"] - else: - val = True - kwargs[key] = val - return kwargs - - def execute(self, cmd): - if not cmd.strip(): - return - try: - func, kwargs = self.get_func_kwargs(cmd) - func(**kwargs) - except JSException as e: - if not settings.expert: - print_error(str(e)) - else: - print_error(traceback.format_exc()) - except Exception: - if not settings.expert: - print_error(noexpert_error(traceback.format_exc())) - else: - print_error(traceback.format_exc()) - - def make_prompt(self): - root = ("class:default", "3sdk>") - while True: - try: - result = self.prompt([root]) - self.execute(result) - except (EOFError, KeyboardInterrupt): - sys.exit(0) - - def prompt(self, msg): - return self._prompt.prompt( - msg, completer=self, validator=self, style=style, bottom_toolbar=self.bottom_toolbar, - ) - - -def run(): - parser = argparse.ArgumentParser() - parser.add_argument("--update", action="store_true", help="Update 3sdk") - parser.add_argument("--expert", action="store_true", help="Run 3sdk in expert mode") - args = parser.parse_args() - settings.expert = args.expert - - if args.update: - update() - else: - shell = Shell() - shell.make_prompt() - - -if __name__ == "__main__": - run() diff --git a/jumpscale/threesdk/__init__.py b/jumpscale/threesdk/__init__.py deleted file mode 100755 index 12a0de91a..000000000 --- a/jumpscale/threesdk/__init__.py +++ /dev/null @@ -1,76 +0,0 @@ -from .threebot import ThreeBot as threebot -import inspect -import cgi -from prompt_toolkit.formatted_text import HTML -from prompt_toolkit.shortcuts import print_formatted_text - -__all__ = ["threebot", "info"] - - -def info(): - print_formatted_text(HTML(_get_doc(__all__))) - - -def _get_doc_line(doc): - if not doc: - return "" - for line in doc.splitlines(): - if line.strip(): - return line.strip() - return "" - - -def _get_doc(root_module, level=0, size=4): - """get a formatted docstring from a module - this will loop over __all__self. - - :param root_module: root module - :type root_module: module - :param level: spacing level, defaults to 0 - :type level: int, optional - :param level: spacing size, defaults to 4 - :type level: int, optional - :return: docstring - :rtype: str - """ - - doc = "" - - if isinstance(root_module, list): - glob = globals() - members = [(name, glob[name]) for name in root_module] - else: - members = inspect.getmembers(root_module) - for name, obj in members: - if name.startswith("_"): - continue - if name[0].lower() != name[0]: - continue - - is_module = not inspect.isroutine(obj) - if is_module and level != 0: - continue - - spaces = " " * level - - if is_module: - doc += f"{spaces}{name}" - elif getattr(obj, "__property__", False): - doc += f"{spaces}{name}" - else: - doc += f"{spaces}{name}" - - if obj.__doc__: - try: - # only get first line of member docstring - first_line = _get_doc_line(obj.__doc__) - doc += cgi.html.escape(f": {first_line}") - except IndexError: - pass - - doc = f"{doc}\n" - - if is_module: - doc += _get_doc(obj, level=level + size) - - return doc diff --git a/jumpscale/threesdk/container.py b/jumpscale/threesdk/container.py deleted file mode 100644 index 45da51b43..000000000 --- a/jumpscale/threesdk/container.py +++ /dev/null @@ -1,76 +0,0 @@ -from jumpscale.clients.docker.docker import DockerClient -from jumpscale.core.dirs.dirs import Dirs -from jumpscale.core.exceptions import Value -from jumpscale.core.executors.local import execute - -docker_client = DockerClient() - - -class Container: - """Container management - """ - - @staticmethod - def install(name, image, development: bool = False, volumes=None): - """Creates a container - - Args: - name (str): name of the container - image (str): container image. - development (bool, optional): if true will mount codedir. Defaults to False. - volumes (dict, optional): paths to be mounted - - Raises: - Value: Container with specified name already exists - """ - if docker_client.exists(name): - raise Value("Container with specified name already exists") - - volumes = volumes or {} - if development: - volumes = {Dirs.CODEDIR: {"bind": "/sandbox/code", "mode": "rw"}} - - print(f"Creating container {name}") - return docker_client.run(name, image, entrypoint="/sbin/my_init", volumes=volumes, detach=True) - - @staticmethod - def start(name): - """Starts an existing container - - Args: - name (str): name of the container - """ - if not docker_client.exists(name): - raise Value("Container with specified name doesn't exist") - docker_client.start(name) - - @staticmethod - def exec(name, cmd): - """Execute command in container - - Args: - name (str): name of the container - cmd (str or list): command to execute - """ - basecmd = ["docker", "exec", "-it", name] - if isinstance(cmd, str): - basecmd.append(cmd) - else: - basecmd += cmd - execute(basecmd, pty=True) - - @staticmethod - def stop(name): - """Stops an existing container - - Args: - name (str): name of the container - """ - if not docker_client.exists(name): - raise Value("Container with specified name doesn't exist") - docker_client.stop(name) - - @staticmethod - def delete(name): - Container.stop(name) - docker_client.delete(name) diff --git a/jumpscale/threesdk/identitymanager.py b/jumpscale/threesdk/identitymanager.py deleted file mode 100644 index e3822235b..000000000 --- a/jumpscale/threesdk/identitymanager.py +++ /dev/null @@ -1,161 +0,0 @@ -import binascii - -import requests -from collections import namedtuple - -from jumpscale.data.encryption import mnemonic -from jumpscale.data.nacl.jsnacl import NACL -from jumpscale.data.serializers import base64 -from jumpscale.core import exceptions -from jumpscale.tools.console import ask_choice, ask_string, printcolors - -NETWORKS = {"mainnet": "explorer.grid.tf", "testnet": "explorer.testnet.grid.tf", "devnet": "explorer.devnet.grid.tf"} - -RESTART_CHOICE = "Restart from the begining" -REENTER_CHOICE = "Re-Enter your value" -CHOICES = [RESTART_CHOICE, REENTER_CHOICE] - -IdentityInfo = namedtuple("IdentityInfo", ["identity", "email", "words", "explorer"]) - - -class IdentityManager: - def __init__(self, identity: str = "", email: str = None, words: str = None, explorer: str = None): - self.identity = identity - self.email = email - self.words = words - self.explorer = explorer - - def reset(self): - self.identity = "" - self.email = "" - self.words = "" - self.explorer = "" - - def _check_keys(self, user_explorer_key, user_app): - if not user_app: - return True - pub_key_app = base64.decode(user_app["publicKey"]) - if binascii.unhexlify(user_explorer_key) != pub_key_app: - return False - return True - - def _get_user(self): - response = requests.get(f"https://login.threefold.me/api/users/{self.identity}") - if response.status_code == 404: - raise exceptions.Value( - "\nThis identity does not exist in 3bot mobile app connect, Please create an idenity first using 3Bot Connect mobile Application\n" - ) - userdata = response.json() - - resp = requests.get("https://{}/explorer/users".format(self.explorer), params={"name": self.identity}) - if resp.status_code == 404 or resp.json() == []: - # creating new user - user = {} - user["name"] = userdata["doublename"] - user["pubkey"] = base64.decode(userdata["publicKey"]).hex() - printcolors( - f"\nWelcome {{CYAN}}{userdata['doublename']}{{WHITE}}. Creating a new record on {{CYAN}}{self.explorer}{{RESET}}.\n" - ) - return user, userdata - else: - users = resp.json() - - if not self._check_keys(users[0]["pubkey"], userdata): - raise exceptions.Value( - f"\nYour 3bot on {self.explorer} seems to have been previously registered with a different public key.\n" - f"The identity of {self.identity} is mismatched with 3bot connect app" - "Please contact support.grid.tf to reset it.\n" - "Note: use the same email registered on the explorer to contact support otherwise we cannot reset the account.\n" - ) - - if users: - return (users[0], userdata) - return None, userdata - - def _check_email(self, email): - resp = requests.get("https://{}/explorer/users".format(self.explorer), params={"email": email}) - users = resp.json() - if users: - if users[0]["name"] == self.identity: - return True - else: - return False - else: - return True - - def ask_identity(self, identity=None, explorer=None): - def _fill_identity_args(identity, explorer): - def fill_words(): - words = ask_string("Copy the phrase from your 3bot Connect app here: ") - self.words = words - - def fill_identity(): - identity = ask_string("what is your threebot name (identity)? ") - if "." not in identity: - identity += ".3bot" - self.identity = identity - - if identity: - if self.identity != identity and self.identity: - self.reset() - self.identity = identity - - if explorer: - self.explorer = explorer - elif not self.explorer: - response = ask_choice( - "Which network would you like to register to? ", ["mainnet", "testnet", "devnet", "none"] - ) - self.explorer = NETWORKS.get(response, None) - if not self.explorer: - return True - - user, user_app = None, None - while not user: - fill_identity() - try: - user, user_app = self._get_user() - except exceptions.Value as e: - response = ask_choice(f"{e}What would you like to do? ", CHOICES) - if response == RESTART_CHOICE: - return False - - while not self.email: - self.email = ask_string("What is the email address associated with your identity? ") - if self._check_email(self.email): - break - else: - self.email = None - response = ask_choice( - "This email is currently associated with another identity. What would you like to do? ", - CHOICES, - ) - if response == RESTART_CHOICE: - return False - - print("Configured email for this identity is {}".format(self.email)) - - # time to do validation of words - hexkey = None - while True: - if not self.words: - fill_words() - try: - seed = mnemonic.mnemonic_to_key(self.words.strip()) - hexkey = NACL(seed).get_verify_key_hex() - if (user and hexkey != user["pubkey"]) or not self._check_keys(hexkey, user_app): - raise Exception - else: - return True - except Exception: - choice = ask_choice( - "\nSeems one or more more words entered is invalid.\nWhat would you like to do? ", CHOICES, - ) - if choice == RESTART_CHOICE: - return False - fill_words() - - while True: - if _fill_identity_args(identity, explorer): - identity_info = IdentityInfo(self.identity, self.email, self.words, self.explorer) - return identity_info diff --git a/jumpscale/threesdk/settings.py b/jumpscale/threesdk/settings.py deleted file mode 100644 index a8d4b36dc..000000000 --- a/jumpscale/threesdk/settings.py +++ /dev/null @@ -1 +0,0 @@ -expert = False diff --git a/jumpscale/threesdk/threebot.py b/jumpscale/threesdk/threebot.py deleted file mode 100644 index 88285e649..000000000 --- a/jumpscale/threesdk/threebot.py +++ /dev/null @@ -1,114 +0,0 @@ -import os - - -from jumpscale.core.config import get_current_version -from jumpscale.core.exceptions import Value -from jumpscale.clients.docker.docker import DockerClient - -from . import settings -from .container import Container -from .identitymanager import IdentityManager - -DEFAULT_CONTAINER_NAME = "3bot-ng" -DEFAULT_IMAGE = "threefoldtech/js-ng" -PERSISTENT_STORE = os.path.expanduser("~/.config/jumpscale/containers") - -docker_client = DockerClient() - - -class ThreeBot(Container): - """ - Manage your threebot - """ - - @staticmethod - def install( - name=None, image=None, identity=None, email=None, words=None, explorer=None, development: bool = None, - ): - """Creates a threebot container - - Args: - name (str, optional): name of the container. Defaults to 3bot-ng - image (str, optional): container image. Defaults to "threefoldtech/js-ng:latest". - identity (str, optional): threebot name. Defaults to None. - email (str, optional): threebot email. Defaults to None. - words (str, optional): seed phrase of the user. Defaults to None. - explorer (str, optional): which explorer network to use: mainnet, testnet, devnet. Defaults to None. - development (bool, optional): if true will mount codedir. Defaults to False. - - Raises: - Value: Container with specified name already exists - Value: explorer not in mainnet, testnet, devnet - """ - if development is None: - development = settings.expert - name = name or DEFAULT_CONTAINER_NAME - current_version = get_current_version() - image = image or f"{DEFAULT_IMAGE}:{current_version}" - - pers_path = f"{PERSISTENT_STORE}/{name}" - configure = not os.path.exists(pers_path) - if configure: - identity = IdentityManager(identity, email, words, explorer) - identity, email, words, explorer = identity.ask_identity() - - os.makedirs(PERSISTENT_STORE, exist_ok=True) - volumes = {pers_path: {"bind": "/root/.config/jumpscale", "mode": "rw"}} - - container = Container.install(name, image, development, volumes) - container.exec_run(["redis-server", "--daemonize yes"]) - - if configure: - container.exec_run(["jsng", f"j.core.identity.new('default', '{identity}', '{email}', '{words}')"]) - container.exec_run(["jsng", "j.core.identity.set_default('default')"]) - - @staticmethod - def jsng(name=DEFAULT_CONTAINER_NAME): - """Get's shell in threebot - - Args: - name (str): name of the container (default: 3bot-ng) - """ - Container.exec(name, "jsng") - - @staticmethod - def shell(name=DEFAULT_CONTAINER_NAME): - """Get's shell in threebot - - Args: - name (str): name of the container (default: 3bot-ng) - """ - Container.exec(name, "bash") - - @staticmethod - def start(name=DEFAULT_CONTAINER_NAME): - """Start threebot container with threebot server - - Args: - name (str): name of the container (default: 3bot-ng) - """ - Container.start(name) - Container.exec(name, ["threebot", "start", "--background"]) - - @staticmethod - def stop(name=DEFAULT_CONTAINER_NAME): - """Stop threebot installation with container - - Args: - name (str): name of the container (default: 3bot-ng) - """ - if name in docker_client.list(): - Container.exec(name, ["threebot", "stop"]) - Container.stop(name) - else: - print("Container is already stopped") - - @staticmethod - def restart(name=DEFAULT_CONTAINER_NAME): - """restart threebot installation with container - - Args: - name (str): name of the container (default: 3bot-ng) - """ - ThreeBot.stop(name=name) - ThreeBot.start(name=name) diff --git a/pyproject.toml b/pyproject.toml index 9ac47f22b..041ace870 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,6 @@ objgraph = "^3.4.1" [tool.poetry.scripts] jsng = "jumpscale.entry_points.jsng:run" -usershell = "jumpscale.entry_points.usershell:run" jsctl = "jumpscale.entry_points.jsctl:cli" jsync = "jumpscale.entry_points.jsync:cli" From ff0a40113ed127d033a66e948a11a1cd594e3f64 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ghanem Date: Mon, 31 Oct 2022 11:49:48 +0200 Subject: [PATCH 5/8] bump version to 11.0b17 (#612) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 041ace870..89937fde8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "js-ng" packages = [{ include = "jumpscale" }] -version = "11.0b16" +version = "11.0b17" description = "system automation, configuration management and RPC framework" authors = ["xmonader "] license = "MIT" From 94d0653f49b88ea17bbe1dc18bbfcd63a1bcea4e Mon Sep 17 00:00:00 2001 From: Abdelrahman Ghanem Date: Fri, 18 Nov 2022 13:43:43 +0200 Subject: [PATCH 6/8] add more internal docs about base classes (#614) --- docs/wiki/internals/baseclasses.md | 110 +++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 docs/wiki/internals/baseclasses.md diff --git a/docs/wiki/internals/baseclasses.md b/docs/wiki/internals/baseclasses.md new file mode 100644 index 000000000..06e61c0cf --- /dev/null +++ b/docs/wiki/internals/baseclasses.md @@ -0,0 +1,110 @@ +# Base classes + +Base classes are intended to provide data modeling, validation and configuration management by utilizing normal python classes. For example, given the following simple class + +```python +class Person: + name = fields.String(default="ahmed") +``` + +The user should be able to create/store multiple instances of `Person` with valid data. + +To achieve this, we implemented base classes using `meta` classes and property descriptors. (a small note, after python `3.7` data classes seems a better option that can be used later). + +At first, to explain why we need to implement custom base class/model, we will illustrate the following examples: + +If we have the same class called `Person`, with the following definition: + + +```python +class Person: + name = fields.String(default="ahmed") +``` + +Accessing the class variable `name` from class or instance level will yield the same value, an instance of `String` field: + +```python +Person.name #=> + +p = Person() +p.name #=> +``` + +The solution to this problem is using data descriptors (see https://docs.python.org/3/howto/descriptor.html), so the following class: + +```python +class Person(Base): + name = fields.String(default="ahmed") +``` + +Should have different behavior when accessing `name` from a class or an objects, so, it will be converted by the meta class to a new class like: + +```python +class Person(Base): + def __init__(self): + self.__name = "ahmed" + + @property + def get_name(self): + return self.__name + + @property + def set_name(self, value): + self.__name == value + + name = property(get_name, set_name) +``` + +And accessing `name` from class and object levels will yield: + +```python +Person.name #=> + + +p = Person() +p.name #=> "ahmed" +``` + +Parent relationship is supported too, every instance can have a parent object (which must be a `Base` type too) + +## BaseMeta + +This [metaclass](https://docs.python.org/3/reference/datamodel.html#metaclasses) will convert normal classes to a new class with "injected" properties. + +Also, this `metaclass` adds all field information inside `_fields` class variable. + + +```python + def __new__(cls, name: str, based: tuple, attrs: dict) -> type: + """ + get a new class with all field attributes replaced by property data descriptors. + + Args: + name (str): class name + based (tuple): super class types (classes) + attrs (dict): current attributes + + Returns: + type: a new class + """ + # will collect class fields + cls_fields = {} + ... + ... +``` + +See complete implementation at [meta.py](https://github.com/threefoldtech/js-ng/blob/fa1582b83c36a8b18094fd208d04499a8d0f289d/jumpscale/core/base/meta.py#L145) + + +## Base + +This base class uses `BaseMeta` as its meta class, hence we have all information about defined fields/properties. + +Then it implements + - Serialization: to_dict/from_dict methods + - Hierarchy: using an optional `parent_` + + +See full implementation at [meta.py](https://github.com/threefoldtech/js-ng/blob/fa1582b83c36a8b18094fd208d04499a8d0f289d/jumpscale/core/base/meta.py#L200) + +Any one who uses this class as base class/model for his type, he will be able to define [custom fields](https://github.com/threefoldtech/js-ng/blob/development/jumpscale/core/base/fields.py) as class variables, then set/get a serializable and validated data. From 074d153a23df512ef870ba16f158582471f9958b Mon Sep 17 00:00:00 2001 From: xmonader Date: Fri, 18 Nov 2022 13:44:19 +0200 Subject: [PATCH 7/8] Update CODEOWNERS --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 346d60620..e7a762480 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @xmonader @rkhamis @abom @waleedhammam @m-motawea @AhmedHanafy725 +* @xmonader @rkhamis @rawdaGastan @AhmedHanafy725 From 1d498aa2e0cbb155e3f13ce1e9dd267b9448dc95 Mon Sep 17 00:00:00 2001 From: Rawda Fawzy <47260239+rawdaGastan@users.noreply.github.com> Date: Sun, 4 Dec 2022 15:36:53 +0200 Subject: [PATCH 8/8] release 11.1 (#617) prepare 11.1 release --- README.md | 1 + docs/api/jumpscale/clients/base.html | 20 +- .../clients/btc_alpha/btc_alpha.html | 892 ++++ .../jumpscale/clients/btc_alpha/index.html | 102 + .../clients/currencylayer/currencies.html | 234 + .../clients/currencylayer/currencylayer.html | 508 ++ .../clients/currencylayer/index.html | 1523 ++++++ .../clients/digitalocean/digitalocean.html | 2911 +++++++++++ .../jumpscale/clients/digitalocean/index.html | 108 + .../clients/digitalocean/project.html | 687 +++ docs/api/jumpscale/clients/docker/docker.html | 2 +- docs/api/jumpscale/clients/docker/index.html | 2 +- docs/api/jumpscale/clients/gedis/gedis.html | 745 +++ docs/api/jumpscale/clients/gedis/index.html | 242 + docs/api/jumpscale/clients/git/git.html | 2 +- docs/api/jumpscale/clients/git/index.html | 2 +- docs/api/jumpscale/clients/github/base.html | 240 + docs/api/jumpscale/clients/github/github.html | 522 ++ docs/api/jumpscale/clients/github/helper.html | 109 + docs/api/jumpscale/clients/github/index.html | 124 + docs/api/jumpscale/clients/github/issue.html | 1138 +++++ .../jumpscale/clients/github/milestone.html | 385 ++ docs/api/jumpscale/clients/github/repo.html | 1673 ++++++ docs/api/jumpscale/clients/gogs/gogs.html | 334 ++ docs/api/jumpscale/clients/gogs/index.html | 99 + docs/api/jumpscale/clients/index.html | 84 +- docs/api/jumpscale/clients/kraken/index.html | 101 + docs/api/jumpscale/clients/kraken/kraken.html | 501 ++ docs/api/jumpscale/clients/liquid/index.html | 101 + docs/api/jumpscale/clients/liquid/liquid.html | 1219 +++++ docs/api/jumpscale/clients/mail/index.html | 101 + docs/api/jumpscale/clients/mail/mail.html | 591 +++ docs/api/jumpscale/clients/name/index.html | 101 + docs/api/jumpscale/clients/name/name.html | 268 + docs/api/jumpscale/clients/redis/index.html | 2 +- docs/api/jumpscale/clients/redis/redis.html | 6 +- docs/api/jumpscale/clients/s3/index.html | 99 + docs/api/jumpscale/clients/s3/s3.html | 743 +++ .../api/jumpscale/clients/sendgrid/index.html | 99 + .../jumpscale/clients/sendgrid/sendgrid.html | 302 ++ .../jumpscale/clients/sshclient/index.html | 2 +- .../clients/sshclient/sshclient.html | 2 +- docs/api/jumpscale/clients/sshkey/index.html | 2 +- docs/api/jumpscale/clients/sshkey/sshkey.html | 2 +- .../jumpscale/clients/stellar/balance.html | 489 ++ .../jumpscale/clients/stellar/exceptions.html | 207 + docs/api/jumpscale/clients/stellar/index.html | 437 ++ .../jumpscale/clients/stellar/stellar.html | 3725 ++++++++++++++ .../clients/stellar/transaction.html | 536 ++ .../jumpscale/clients/stellar/vesting.html | 230 + .../jumpscale/clients/stellar/wrapped.html | 303 ++ .../jumpscale/clients/syncthing/index.html | 101 + .../clients/syncthing/syncthing.html | 802 +++ docs/api/jumpscale/clients/taiga/index.html | 106 + docs/api/jumpscale/clients/taiga/models.html | 3085 +++++++++++ docs/api/jumpscale/clients/taiga/taiga.html | 4499 +++++++++++++++++ docs/api/jumpscale/clients/zdb/client.html | 2 +- docs/api/jumpscale/clients/zdb/index.html | 2 +- .../api/jumpscale/clients/zerotier/index.html | 101 + .../jumpscale/clients/zerotier/zerotier.html | 1095 ++++ .../core/application/application.html | 2 +- .../api/jumpscale/core/application/index.html | 2 +- docs/api/jumpscale/core/base/events.html | 3 +- docs/api/jumpscale/core/base/factory.html | 17 +- docs/api/jumpscale/core/base/fields.html | 2 +- docs/api/jumpscale/core/base/index.html | 2 +- docs/api/jumpscale/core/base/meta.html | 27 +- .../jumpscale/core/base/store/filesystem.html | 2 +- docs/api/jumpscale/core/base/store/index.html | 2 +- docs/api/jumpscale/core/base/store/redis.html | 2 +- .../core/base/store/serializers.html | 2 +- .../jumpscale/core/base/store/whooshfts.html | 2 +- docs/api/jumpscale/core/config/config.html | 2 +- docs/api/jumpscale/core/config/index.html | 2 +- docs/api/jumpscale/core/db/index.html | 2 +- docs/api/jumpscale/core/dirs/dirs.html | 54 +- docs/api/jumpscale/core/dirs/index.html | 2 +- docs/api/jumpscale/core/events/index.html | 2 +- .../jumpscale/core/exceptions/exceptions.html | 8 +- docs/api/jumpscale/core/exceptions/index.html | 2 +- .../core/executors/command_builder.html | 2 +- docs/api/jumpscale/core/executors/index.html | 2 +- docs/api/jumpscale/core/executors/local.html | 2 +- docs/api/jumpscale/core/executors/remote.html | 2 +- docs/api/jumpscale/core/executors/tmux.html | 2 +- docs/api/jumpscale/core/identity/index.html | 1051 ++++ docs/api/jumpscale/core/index.html | 7 +- docs/api/jumpscale/core/logging/index.html | 2 +- docs/api/jumpscale/core/logging/logging.html | 2 +- docs/api/jumpscale/data/bcdb/bcdb.html | 156 +- docs/api/jumpscale/data/bcdb/clients.html | 2 +- docs/api/jumpscale/data/bcdb/dumpsql.html | 2 +- docs/api/jumpscale/data/bcdb/flush.html | 2 +- docs/api/jumpscale/data/bcdb/index.html | 2 +- docs/api/jumpscale/data/bcdb/interfaces.html | 4 +- docs/api/jumpscale/data/bcdb/models/base.html | 2 +- .../jumpscale/data/bcdb/models/db_model.html | 2 +- .../data/bcdb/models/emplyee_model.html | 2 +- .../api/jumpscale/data/bcdb/models/index.html | 2 +- .../data/bcdb/models/model_model.html | 2 +- .../data/bcdb/models/post_model.html | 2 +- .../data/bcdb/models/proj_model.html | 2 +- .../data/bcdb/models/test_model.html | 2 +- .../data/bcdb/models/user_model.html | 2 +- docs/api/jumpscale/data/cache/index.html | 2 +- docs/api/jumpscale/data/countries/index.html | 141 + .../jumpscale/data/encryption/exceptions.html | 2 +- docs/api/jumpscale/data/encryption/index.html | 2 +- .../jumpscale/data/encryption/mnemonic.html | 2 +- .../jumpscale/data/encryption/wordlist.html | 2 +- docs/api/jumpscale/data/fake/index.html | 2 +- docs/api/jumpscale/data/hash/hash.html | 24 +- docs/api/jumpscale/data/hash/index.html | 2 +- .../data/idgenerator/idgenerator.html | 22 +- .../api/jumpscale/data/idgenerator/index.html | 2 +- docs/api/jumpscale/data/index.html | 14 +- docs/api/jumpscale/data/inifile/index.html | 2 +- docs/api/jumpscale/data/inifile/inifile.html | 2 +- docs/api/jumpscale/data/nacl/index.html | 2 +- docs/api/jumpscale/data/nacl/jsnacl.html | 2 +- docs/api/jumpscale/data/platform/index.html | 2 +- .../jumpscale/data/random_names/index.html | 10 +- docs/api/jumpscale/data/schema/index.html | 2 +- docs/api/jumpscale/data/schema/schema.html | 2 +- .../jumpscale/data/serializers/base64.html | 2 +- docs/api/jumpscale/data/serializers/dill.html | 2 +- .../api/jumpscale/data/serializers/index.html | 22 +- docs/api/jumpscale/data/serializers/json.html | 2 +- docs/api/jumpscale/data/serializers/lzma.html | 18 +- .../jumpscale/data/serializers/msgpack.html | 26 +- .../jumpscale/data/serializers/pickle.html | 26 +- docs/api/jumpscale/data/serializers/toml.html | 26 +- docs/api/jumpscale/data/serializers/yaml.html | 26 +- docs/api/jumpscale/data/tarfile/index.html | 2 +- docs/api/jumpscale/data/tarfile/tar_file.html | 14 +- .../jumpscale/data/terminaltable/index.html | 8 +- docs/api/jumpscale/data/text/index.html | 12 +- docs/api/jumpscale/data/time/index.html | 10 +- .../data/treemanager/exceptions.html | 147 + .../api/jumpscale/data/treemanager/index.html | 80 + .../data/treemanager/treemanager.html | 1222 +++++ docs/api/jumpscale/data/types/index.html | 2 +- docs/api/jumpscale/data/types/pritypes.html | 86 +- docs/api/jumpscale/data/types/types.html | 2 +- docs/api/jumpscale/entry_points/index.html | 7 +- docs/api/jumpscale/entry_points/jsctl.html | 2 +- docs/api/jumpscale/entry_points/jsng.html | 2 +- docs/api/jumpscale/entry_points/jsync.html | 2 +- docs/api/jumpscale/entry_points/threebot.html | 465 ++ docs/api/jumpscale/index.html | 12 +- .../install/certbot/certbot_cronjob.html | 240 + docs/api/jumpscale/install/certbot/index.html | 68 + .../jumpscale/install/codeserver_install.html | 66 + docs/api/jumpscale/install/index.html | 70 + docs/api/jumpscale/loader.html | 12 +- .../packages/admin/actors/admin.html | 2196 ++++++++ .../packages/admin/actors/alerts.html | 396 ++ .../packages/admin/actors/health.html | 554 ++ .../packages/admin/actors/identity.html | 311 ++ .../packages/admin/actors/index.html | 95 + .../jumpscale/packages/admin/actors/logs.html | 254 + .../packages/admin/actors/packages.html | 677 +++ .../packages/admin/actors/wallet.html | 781 +++ .../packages/admin/bottle/admin.html | 279 + .../packages/admin/bottle/index.html | 70 + .../packages/admin/bottle/models.html | 185 + docs/api/jumpscale/packages/admin/index.html | 83 + .../admin/services/alerts_notifier.html | 153 + .../packages/admin/services/diskcheck.html | 133 + .../packages/admin/services/heartbeat.html | 188 + .../packages/admin/services/index.html | 85 + .../packages/admin/services/notifier.html | 253 + .../packages/admin/services/stellar.html | 166 + .../jumpscale/packages/auth/bottle/auth.html | 981 ++++ .../jumpscale/packages/auth/bottle/index.html | 65 + docs/api/jumpscale/packages/auth/index.html | 162 + .../packages/backup/actors/index.html | 65 + .../packages/backup/actors/minio.html | 627 +++ docs/api/jumpscale/packages/backup/index.html | 76 + .../packages/backup/services/index.html | 66 + .../backup/services/system_backup.html | 331 ++ .../api/jumpscale/packages/billing/index.html | 65 + .../packages/billing/services/billing.html | 125 + .../packages/billing/services/index.html | 65 + .../packages/chatflows/actors/chatbot.html | 703 +++ .../packages/chatflows/actors/index.html | 65 + .../packages/chatflows/bottle/bottle.html | 134 + .../packages/chatflows/bottle/index.html | 65 + .../jumpscale/packages/chatflows/index.html | 70 + .../jumpscale/packages/codeserver/index.html | 65 + .../packages/codeserver/package.html | 271 + docs/api/jumpscale/packages/index.html | 110 + .../jumpscale/packages/notebooks/index.html | 65 + .../jumpscale/packages/notebooks/package.html | 386 ++ .../packages/polls/bottle/index.html | 83 + .../packages/polls/bottle/polls_bottle.html | 102 + .../packages/polls/chats/example2.html | 1024 ++++ .../jumpscale/packages/polls/chats/foo.html | 221 + .../jumpscale/packages/polls/chats/index.html | 80 + .../jumpscale/packages/polls/chats/new.html | 516 ++ .../packages/polls/chats/threefold.html | 648 +++ docs/api/jumpscale/packages/polls/index.html | 144 + .../api/jumpscale/packages/polls/package.html | 122 + .../packages/stellar_stats/bottle/index.html | 70 + .../stellar_stats/bottle/stats_service.html | 477 ++ .../stellar_stats/bottle/stellar_stats.html | 272 + .../packages/stellar_stats/index.html | 65 + .../api/jumpscale/packages/weblibs/index.html | 65 + .../jumpscale/packages/weblibs/package.html | 274 + .../jumpscale/sals/backupjob/backupjob.html | 868 ++++ docs/api/jumpscale/sals/backupjob/index.html | 100 + docs/api/jumpscale/sals/billing/billing.html | 634 +++ docs/api/jumpscale/sals/billing/index.html | 103 + docs/api/jumpscale/sals/billing/models.html | 1887 +++++++ .../jumpscale/sals/chatflows/chatflows.html | 2910 +++++++++++ docs/api/jumpscale/sals/chatflows/index.html | 107 + .../sals/chatflows/models/index.html | 65 + .../sals/chatflows/models/voter_model.html | 424 ++ docs/api/jumpscale/sals/chatflows/polls.html | 901 ++++ docs/api/jumpscale/sals/crtsh/index.html | 335 ++ docs/api/jumpscale/sals/fs/index.html | 2 +- docs/api/jumpscale/sals/hostsfile/index.html | 8 +- docs/api/jumpscale/sals/index.html | 32 +- docs/api/jumpscale/sals/nettools/index.html | 2 +- docs/api/jumpscale/sals/nginx/index.html | 104 + docs/api/jumpscale/sals/nginx/nginx.html | 3631 +++++++++++++ docs/api/jumpscale/sals/nginx/utils.html | 88 + .../api/jumpscale/sals/nginx_proxy/index.html | 924 ++++ docs/api/jumpscale/sals/process/index.html | 2 +- docs/api/jumpscale/sals/testdocs/index.html | 2 +- docs/api/jumpscale/sals/unix/index.html | 2 +- docs/api/jumpscale/sals/unix/user.html | 2 +- .../jumpscale/servers/appserver/index.html | 167 + .../jumpscale/servers/gedis/baseactor.html | 350 ++ .../servers/gedis/example_actor.html | 481 ++ docs/api/jumpscale/servers/gedis/index.html | 214 + docs/api/jumpscale/servers/gedis/server.html | 1091 ++++ .../jumpscale/servers/gedis/systemactor.html | 392 ++ .../jumpscale/servers/gedis_http/index.html | 507 ++ docs/api/jumpscale/servers/index.html | 22 +- .../jumpscale/servers/openresty/index.html | 2 +- .../jumpscale/servers/openresty/location.html | 2 +- .../jumpscale/servers/openresty/server.html | 2 +- .../jumpscale/servers/openresty/utils.html | 2 +- docs/api/jumpscale/servers/rack/index.html | 2 +- docs/api/jumpscale/servers/rack/rack.html | 2 +- .../api/jumpscale/servers/threebot/index.html | 288 ++ .../jumpscale/servers/threebot/threebot.html | 3483 +++++++++++++ docs/api/jumpscale/shell/config.html | 2 +- docs/api/jumpscale/shell/index.html | 2 +- .../tools/alerthandler/alerthandler.html | 2 +- .../jumpscale/tools/alerthandler/index.html | 2 +- .../api/jumpscale/tools/codeloader/index.html | 2 +- docs/api/jumpscale/tools/console/index.html | 82 +- .../tools/depsresolver/depsresolver.html | 2 +- .../jumpscale/tools/depsresolver/index.html | 2 +- docs/api/jumpscale/tools/dnstool/index.html | 273 + .../tools/errorhandler/errorhandler.html | 2 +- .../jumpscale/tools/errorhandler/index.html | 2 +- docs/api/jumpscale/tools/export/index.html | 131 + docs/api/jumpscale/tools/git/index.html | 2 +- .../jumpscale/tools/highlighter/index.html | 14 +- docs/api/jumpscale/tools/http/index.html | 2 +- docs/api/jumpscale/tools/index.html | 57 +- docs/api/jumpscale/tools/jinja2/index.html | 2 +- docs/api/jumpscale/tools/keygen/index.html | 2 +- docs/api/jumpscale/tools/keygen/keygen.html | 2 +- docs/api/jumpscale/tools/nginx/index.html | 169 + .../jumpscale/tools/nginx/nginxserver.html | 508 ++ .../tools/notificationsqueue/index.html | 97 + .../tools/notificationsqueue/queue.html | 578 +++ .../jumpscale/tools/poolexecutor/index.html | 89 + .../tools/poolexecutor/poolexecutor.html | 351 ++ docs/api/jumpscale/tools/qrcode/index.html | 235 + docs/api/jumpscale/tools/redis/index.html | 99 + docs/api/jumpscale/tools/redis/redis.html | 354 ++ docs/api/jumpscale/tools/restic/index.html | 99 + docs/api/jumpscale/tools/restic/restic.html | 1249 +++++ .../api/jumpscale/tools/schemac/compiler.html | 2 +- docs/api/jumpscale/tools/schemac/index.html | 8 +- .../tools/schemac/plugins/crystal.html | 2 +- .../tools/schemac/plugins/index.html | 2 +- .../jumpscale/tools/schemac/plugins/jsng.html | 2 +- .../tools/schemac/plugins/plugin.html | 2 +- .../tools/schemac/plugins/utils.html | 2 +- .../jumpscale/tools/servicemanager/index.html | 99 + .../tools/servicemanager/servicemanager.html | 809 +++ .../api/jumpscale/tools/startupcmd/index.html | 2 +- .../tools/startupcmd/startupcmd.html | 2 +- docs/api/jumpscale/tools/syncer/index.html | 2 +- docs/api/jumpscale/tools/tfgateway/index.html | 425 ++ docs/api/jumpscale/tools/timer/index.html | 2 +- docs/api/jumpscale/tools/wireguard/index.html | 170 + docs/wiki/baseclasses.md | 13 +- jumpscale/clients/redis/redis.py | 2 +- jumpscale/core/base/factory.py | 2 +- jumpscale/core/base/store/whooshfts.py | 4 +- jumpscale/core/config/__init__.py | 2 +- jumpscale/core/config/config.py | 9 +- jumpscale/core/exceptions/__init__.py | 2 +- jumpscale/core/logging/logging.py | 8 +- jumpscale/data/bcdb/__init__.py | 2 +- jumpscale/data/bcdb/bcdb.py | 79 +- jumpscale/data/bcdb/interfaces.py | 11 +- jumpscale/data/bcdb/models/__init__.py | 4 +- jumpscale/data/bcdb/models/db_model.py | 3 +- jumpscale/data/bcdb/models/emplyee_model.py | 3 +- jumpscale/data/bcdb/models/post_model.py | 3 +- jumpscale/data/bcdb/models/proj_model.py | 3 +- jumpscale/data/bcdb/models/test_model.py | 3 +- jumpscale/data/bcdb/models/user_model.py | 3 +- jumpscale/data/cache/__init__.py | 2 +- jumpscale/data/idgenerator/__init__.py | 2 +- jumpscale/data/inifile/inifile.py | 1 - jumpscale/data/serializers/dill.py | 2 +- jumpscale/data/serializers/lzma.py | 12 +- jumpscale/data/serializers/msgpack.py | 12 +- jumpscale/data/serializers/pickle.py | 12 +- jumpscale/data/serializers/toml.py | 13 +- jumpscale/data/serializers/yaml.py | 13 +- jumpscale/data/text/__init__.py | 7 +- jumpscale/loader.py | 2 +- jumpscale/servers/openresty/server.py | 13 +- jumpscale/servers/rack/__init__.py | 2 +- jumpscale/tools/alerthandler/__init__.py | 1 + jumpscale/tools/jinja2/__init__.py | 12 +- jumpscale/tools/keygen/keygen.py | 2 +- jumpscale/tools/schemac/__init__.py | 1 + pyproject.toml | 2 +- tests/core/executors/test_local.py | 16 +- tests/sals/fs/test_fs.py | 2 +- tests/sals/nettools/test_nettools.py | 34 +- 332 files changed, 78331 insertions(+), 626 deletions(-) create mode 100644 docs/api/jumpscale/clients/btc_alpha/btc_alpha.html create mode 100644 docs/api/jumpscale/clients/btc_alpha/index.html create mode 100644 docs/api/jumpscale/clients/currencylayer/currencies.html create mode 100644 docs/api/jumpscale/clients/currencylayer/currencylayer.html create mode 100644 docs/api/jumpscale/clients/currencylayer/index.html create mode 100644 docs/api/jumpscale/clients/digitalocean/digitalocean.html create mode 100644 docs/api/jumpscale/clients/digitalocean/index.html create mode 100644 docs/api/jumpscale/clients/digitalocean/project.html create mode 100644 docs/api/jumpscale/clients/gedis/gedis.html create mode 100644 docs/api/jumpscale/clients/gedis/index.html create mode 100644 docs/api/jumpscale/clients/github/base.html create mode 100644 docs/api/jumpscale/clients/github/github.html create mode 100644 docs/api/jumpscale/clients/github/helper.html create mode 100644 docs/api/jumpscale/clients/github/index.html create mode 100644 docs/api/jumpscale/clients/github/issue.html create mode 100644 docs/api/jumpscale/clients/github/milestone.html create mode 100644 docs/api/jumpscale/clients/github/repo.html create mode 100644 docs/api/jumpscale/clients/gogs/gogs.html create mode 100644 docs/api/jumpscale/clients/gogs/index.html create mode 100644 docs/api/jumpscale/clients/kraken/index.html create mode 100644 docs/api/jumpscale/clients/kraken/kraken.html create mode 100644 docs/api/jumpscale/clients/liquid/index.html create mode 100644 docs/api/jumpscale/clients/liquid/liquid.html create mode 100644 docs/api/jumpscale/clients/mail/index.html create mode 100644 docs/api/jumpscale/clients/mail/mail.html create mode 100644 docs/api/jumpscale/clients/name/index.html create mode 100644 docs/api/jumpscale/clients/name/name.html create mode 100644 docs/api/jumpscale/clients/s3/index.html create mode 100644 docs/api/jumpscale/clients/s3/s3.html create mode 100644 docs/api/jumpscale/clients/sendgrid/index.html create mode 100644 docs/api/jumpscale/clients/sendgrid/sendgrid.html create mode 100644 docs/api/jumpscale/clients/stellar/balance.html create mode 100644 docs/api/jumpscale/clients/stellar/exceptions.html create mode 100644 docs/api/jumpscale/clients/stellar/index.html create mode 100644 docs/api/jumpscale/clients/stellar/stellar.html create mode 100644 docs/api/jumpscale/clients/stellar/transaction.html create mode 100644 docs/api/jumpscale/clients/stellar/vesting.html create mode 100644 docs/api/jumpscale/clients/stellar/wrapped.html create mode 100644 docs/api/jumpscale/clients/syncthing/index.html create mode 100644 docs/api/jumpscale/clients/syncthing/syncthing.html create mode 100644 docs/api/jumpscale/clients/taiga/index.html create mode 100644 docs/api/jumpscale/clients/taiga/models.html create mode 100644 docs/api/jumpscale/clients/taiga/taiga.html create mode 100644 docs/api/jumpscale/clients/zerotier/index.html create mode 100644 docs/api/jumpscale/clients/zerotier/zerotier.html create mode 100644 docs/api/jumpscale/core/identity/index.html create mode 100644 docs/api/jumpscale/data/countries/index.html create mode 100644 docs/api/jumpscale/data/treemanager/exceptions.html create mode 100644 docs/api/jumpscale/data/treemanager/index.html create mode 100644 docs/api/jumpscale/data/treemanager/treemanager.html create mode 100644 docs/api/jumpscale/entry_points/threebot.html create mode 100644 docs/api/jumpscale/install/certbot/certbot_cronjob.html create mode 100644 docs/api/jumpscale/install/certbot/index.html create mode 100644 docs/api/jumpscale/install/codeserver_install.html create mode 100644 docs/api/jumpscale/install/index.html create mode 100644 docs/api/jumpscale/packages/admin/actors/admin.html create mode 100644 docs/api/jumpscale/packages/admin/actors/alerts.html create mode 100644 docs/api/jumpscale/packages/admin/actors/health.html create mode 100644 docs/api/jumpscale/packages/admin/actors/identity.html create mode 100644 docs/api/jumpscale/packages/admin/actors/index.html create mode 100644 docs/api/jumpscale/packages/admin/actors/logs.html create mode 100644 docs/api/jumpscale/packages/admin/actors/packages.html create mode 100644 docs/api/jumpscale/packages/admin/actors/wallet.html create mode 100644 docs/api/jumpscale/packages/admin/bottle/admin.html create mode 100644 docs/api/jumpscale/packages/admin/bottle/index.html create mode 100644 docs/api/jumpscale/packages/admin/bottle/models.html create mode 100644 docs/api/jumpscale/packages/admin/index.html create mode 100644 docs/api/jumpscale/packages/admin/services/alerts_notifier.html create mode 100644 docs/api/jumpscale/packages/admin/services/diskcheck.html create mode 100644 docs/api/jumpscale/packages/admin/services/heartbeat.html create mode 100644 docs/api/jumpscale/packages/admin/services/index.html create mode 100644 docs/api/jumpscale/packages/admin/services/notifier.html create mode 100644 docs/api/jumpscale/packages/admin/services/stellar.html create mode 100644 docs/api/jumpscale/packages/auth/bottle/auth.html create mode 100644 docs/api/jumpscale/packages/auth/bottle/index.html create mode 100644 docs/api/jumpscale/packages/auth/index.html create mode 100644 docs/api/jumpscale/packages/backup/actors/index.html create mode 100644 docs/api/jumpscale/packages/backup/actors/minio.html create mode 100644 docs/api/jumpscale/packages/backup/index.html create mode 100644 docs/api/jumpscale/packages/backup/services/index.html create mode 100644 docs/api/jumpscale/packages/backup/services/system_backup.html create mode 100644 docs/api/jumpscale/packages/billing/index.html create mode 100644 docs/api/jumpscale/packages/billing/services/billing.html create mode 100644 docs/api/jumpscale/packages/billing/services/index.html create mode 100644 docs/api/jumpscale/packages/chatflows/actors/chatbot.html create mode 100644 docs/api/jumpscale/packages/chatflows/actors/index.html create mode 100644 docs/api/jumpscale/packages/chatflows/bottle/bottle.html create mode 100644 docs/api/jumpscale/packages/chatflows/bottle/index.html create mode 100644 docs/api/jumpscale/packages/chatflows/index.html create mode 100644 docs/api/jumpscale/packages/codeserver/index.html create mode 100644 docs/api/jumpscale/packages/codeserver/package.html create mode 100644 docs/api/jumpscale/packages/index.html create mode 100644 docs/api/jumpscale/packages/notebooks/index.html create mode 100644 docs/api/jumpscale/packages/notebooks/package.html create mode 100644 docs/api/jumpscale/packages/polls/bottle/index.html create mode 100644 docs/api/jumpscale/packages/polls/bottle/polls_bottle.html create mode 100644 docs/api/jumpscale/packages/polls/chats/example2.html create mode 100644 docs/api/jumpscale/packages/polls/chats/foo.html create mode 100644 docs/api/jumpscale/packages/polls/chats/index.html create mode 100644 docs/api/jumpscale/packages/polls/chats/new.html create mode 100644 docs/api/jumpscale/packages/polls/chats/threefold.html create mode 100644 docs/api/jumpscale/packages/polls/index.html create mode 100644 docs/api/jumpscale/packages/polls/package.html create mode 100644 docs/api/jumpscale/packages/stellar_stats/bottle/index.html create mode 100644 docs/api/jumpscale/packages/stellar_stats/bottle/stats_service.html create mode 100644 docs/api/jumpscale/packages/stellar_stats/bottle/stellar_stats.html create mode 100644 docs/api/jumpscale/packages/stellar_stats/index.html create mode 100644 docs/api/jumpscale/packages/weblibs/index.html create mode 100644 docs/api/jumpscale/packages/weblibs/package.html create mode 100644 docs/api/jumpscale/sals/backupjob/backupjob.html create mode 100644 docs/api/jumpscale/sals/backupjob/index.html create mode 100644 docs/api/jumpscale/sals/billing/billing.html create mode 100644 docs/api/jumpscale/sals/billing/index.html create mode 100644 docs/api/jumpscale/sals/billing/models.html create mode 100644 docs/api/jumpscale/sals/chatflows/chatflows.html create mode 100644 docs/api/jumpscale/sals/chatflows/index.html create mode 100644 docs/api/jumpscale/sals/chatflows/models/index.html create mode 100644 docs/api/jumpscale/sals/chatflows/models/voter_model.html create mode 100644 docs/api/jumpscale/sals/chatflows/polls.html create mode 100644 docs/api/jumpscale/sals/crtsh/index.html create mode 100644 docs/api/jumpscale/sals/nginx/index.html create mode 100644 docs/api/jumpscale/sals/nginx/nginx.html create mode 100644 docs/api/jumpscale/sals/nginx/utils.html create mode 100644 docs/api/jumpscale/sals/nginx_proxy/index.html create mode 100644 docs/api/jumpscale/servers/appserver/index.html create mode 100644 docs/api/jumpscale/servers/gedis/baseactor.html create mode 100644 docs/api/jumpscale/servers/gedis/example_actor.html create mode 100644 docs/api/jumpscale/servers/gedis/index.html create mode 100644 docs/api/jumpscale/servers/gedis/server.html create mode 100644 docs/api/jumpscale/servers/gedis/systemactor.html create mode 100644 docs/api/jumpscale/servers/gedis_http/index.html create mode 100644 docs/api/jumpscale/servers/threebot/index.html create mode 100644 docs/api/jumpscale/servers/threebot/threebot.html create mode 100644 docs/api/jumpscale/tools/dnstool/index.html create mode 100644 docs/api/jumpscale/tools/export/index.html create mode 100644 docs/api/jumpscale/tools/nginx/index.html create mode 100644 docs/api/jumpscale/tools/nginx/nginxserver.html create mode 100644 docs/api/jumpscale/tools/notificationsqueue/index.html create mode 100644 docs/api/jumpscale/tools/notificationsqueue/queue.html create mode 100644 docs/api/jumpscale/tools/poolexecutor/index.html create mode 100644 docs/api/jumpscale/tools/poolexecutor/poolexecutor.html create mode 100644 docs/api/jumpscale/tools/qrcode/index.html create mode 100644 docs/api/jumpscale/tools/redis/index.html create mode 100644 docs/api/jumpscale/tools/redis/redis.html create mode 100644 docs/api/jumpscale/tools/restic/index.html create mode 100644 docs/api/jumpscale/tools/restic/restic.html create mode 100644 docs/api/jumpscale/tools/servicemanager/index.html create mode 100644 docs/api/jumpscale/tools/servicemanager/servicemanager.html create mode 100644 docs/api/jumpscale/tools/tfgateway/index.html create mode 100644 docs/api/jumpscale/tools/wireguard/index.html diff --git a/README.md b/README.md index ba5f0bc20..cdb80717b 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ python3 -m pip install setuptools -U - make sure the version is bumped in `pyproject.toml` file - generate docs using `make docs` - make sure to call `poetry build` +- enter your api token `poetry config pypi-token.pypi your-api-token` - then publish to pypi using `poetry publish` (note that this requires to be on the publisher account) - now a [release can be added](https://github.com/threefoldtech/js-ng/releases/new) with a tag on master branch. diff --git a/docs/api/jumpscale/clients/base.html b/docs/api/jumpscale/clients/base.html index f250b48a0..212e43a60 100644 --- a/docs/api/jumpscale/clients/base.html +++ b/docs/api/jumpscale/clients/base.html @@ -81,12 +81,30 @@

    Ancestors

    Subclasses

    Inherited members

      @@ -127,4 +145,4 @@

      pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/btc_alpha/btc_alpha.html b/docs/api/jumpscale/clients/btc_alpha/btc_alpha.html new file mode 100644 index 000000000..cc39db4fb --- /dev/null +++ b/docs/api/jumpscale/clients/btc_alpha/btc_alpha.html @@ -0,0 +1,892 @@ + + + + + + +jumpscale.clients.btc_alpha.btc_alpha API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.btc_alpha.btc_alpha

      +
      +
      +

      BTC_ALPHA client

      +

      interacts with btc-alpha.com to provides trading operations and broadcasting of all trading events.

      +

      requirments

      +

      you need a key and a secret for your account. +- make an account from: https://btc-alpha.com/en/accounts/api/settings/ +- go to settings - api and create api key

      +

      make a new client

      +
      > test_client = j.clients.btc_alpha.get("my_client", key_="*******", secret_="*****")
      +> test_client.save()
      +
      +

      use the client

      +
        +
      • you can inquire, exchange, order.
      • +
      • example: test_client.get_currencies()
        +also pprint for more readable response
      • +
      +
      from pprint import pprint
      +pprint(test_client.get_currencies())
      +
      +

      see what other methods do at: https://btc-alpha.github.io/api-docs

      +
      + +Expand source code + +
      """
      +## BTC_ALPHA client
      +interacts with btc-alpha.com to provides trading operations and broadcasting of all trading events.
      +
      +### requirments
      +you need a key and a secret for your account.
      +- make an account from: https://btc-alpha.com/en/accounts/api/settings/
      +- go to settings - api and create api key
      +
      +### make a new client
      +```
      +> test_client = j.clients.btc_alpha.get("my_client", key_="*******", secret_="*****")
      +> test_client.save()
      +```
      +
      +### use the client
      +- you can inquire, exchange, order.
      +* example: `test_client.get_currencies()` <br/>
      +also pprint for more readable response
      +```
      +from pprint import pprint
      +pprint(test_client.get_currencies())
      +```
      +see what other methods do at: https://btc-alpha.github.io/api-docs
      +
      +"""
      +
      +import hmac
      +from time import time
      +from urllib.parse import urlencode
      +
      +import requests
      +from jumpscale.clients.base import Client
      +from jumpscale.core.base import fields
      +from jumpscale.loader import j
      +
      +
      +class BTCAlpha(Client):
      +    _url = fields.String(default="https://btc-alpha.com/api/")
      +    token_id = fields.String(default="")
      +    token_secret = fields.Secret(default="")
      +
      +    def get_currencies(self):
      +        """Returns all active currencies
      +        :return: array of currencies
      +        Example: [{'sign': 'Ƀ', 'short_name': 'BTC'}, {'sign': 'Ξ', 'short_name': 'ETH'}, ...]
      +        """
      +        return self._query("get", "v1/currencies/")
      +
      +    def get_pairs(self, **kwargs):
      +        """Returns all active pairs
      +        :param kwargs: Filters (Optional): currency1, currency2
      +        :return: pairs array
      +        Example: [{'currency2': 'USD', 'maximum_order_size': '100000000.00000000', 'minimum_order_size': '0.00000001',
      +                'currency1': 'BTC', 'name': 'BTC_USD', 'price_precision': 3}, ... ]
      +        """
      +        return self._query("get", "v1/pairs/", params=kwargs)
      +
      +    def get_wallets(self, **kwargs):
      +        """Returns own wallets
      +        :param kwargs: Filters (Optional): currency_id
      +        :return: wallets array
      +        Example: [{'currency': 'BTC', 'balance': '0.00000000', 'reserve': '0.00000000'}, ...]
      +        """
      +        return self._query("get", "v1/wallets/", params=kwargs, auth=True)
      +
      +    def get_own_sell_orders(self, **kwargs):
      +        """ Returns own sell orders """
      +        kwargs["type"] = "sell"
      +        return self._query("get", "v1/orders/own/", params=kwargs, auth=True)
      +
      +    def get_own_buy_orders(self, **kwargs):
      +        """ Returns own buy orders """
      +        kwargs["type"] = "buy"
      +        return self._query("get", "v1/orders/own/", params=kwargs, auth=True)
      +
      +    def create_sell_order(self, pair, amount, price):
      +        """Create sell order
      +        :param pair: Pair (BTC_USD,)
      +        :param amount: Amount of order - string
      +        :param price: Price of order - string
      +        :return: order info
      +        Example: {
      +        'type': 'buy', 'date': 1483721079.51632, 'oid': '11268', 'price': '870.69000000', 'amount': '0.00000000',
      +        'trades': [{'type': 'sell', 'price': '870.69000000', 'o_id': '11266', 'amount': '0.00010000', 'tid': '6049'}]
      +        }
      +        """
      +        data = {"pair": pair, "amount": amount, "price": price, "type": "sell"}
      +        return self._query("post", "v1/order/", data=data, auth=True)
      +
      +    def create_buy_order(self, pair, amount, price):
      +        """ Create buy order """
      +        data = {"pair": pair, "amount": amount, "price": price, "type": "buy"}
      +        return self._query("post", "v1/order/", data=data, auth=True)
      +
      +    def cancel_order(self, oid):
      +        """Cancel order
      +        :param oid: id of order
      +        Example: {
      +            order: 63568
      +        }
      +        """
      +        data = {"order": oid}
      +        return self._query("post", "v1/order-cancel/", data=data, auth=True)
      +
      +    def get_exchanges(self, **kwargs):
      +        """Returns last exchanges
      +        :param kwargs: Filters (Optional): pair, limit
      +        :return: exchanges array
      +        Example: [{'id': 6030, 'price': '839.36000000', 'pair': 'BTC_USD', 'type': 1, 'timestamp': 1483705817.735508,
      +                'amount': '0.00281167'}, ....]
      +        """
      +        return self._query("get", "v1/exchanges/", params=kwargs)
      +
      +    def get_own_exchanges(self, **kwargs):
      +        """Returns only own exchanges
      +        :param kwargs: see get_exchanges()
      +        :return: see get_exchanges()
      +        """
      +        return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +    def get_own_sell_exchanges(self, **kwargs):
      +        kwargs["type"] = "sell"
      +        return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +    def get_own_buy_exchanges(self, **kwargs):
      +        kwargs["type"] = "buy"
      +        return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +    def get_deposits(self):
      +        """Returns all made deposits
      +        :return: deposits array
      +        Example [{'timestamp': 1485363039.18359, 'id': 317, 'currency': 'BTC', 'amount': '530.00000000'}, ... ]
      +        """
      +        return self._query("get", "v1/deposits/", auth=True)
      +
      +    def get_withdraws(self, **kwargs):
      +        """Returns all made withdraws
      +        :param kwargs: currency_id, status
      +        :return: withdraws array
      +        Example: [{'id': 403, 'timestamp': 1485363466.868539, 'currency': 'BTC', 'amount': '0.53000000', 'status': 20} ...]
      +        """
      +
      +        return self._query("get", "v1/withdraws/", params=kwargs, auth=True)
      +
      +    def get_orderbook(self, pair, **kwargs):
      +        """Return sell and buy orders for specified pair
      +        :param kwargs: limit_bids, limit_asks, group
      +        :return: object {'bids': Array, 'asks': Array}
      +        Example: {
      +            'bids': [{'price': 911.519, 'id': 44667, 'amount': 0.000446, 'timestamp': 1485777324.410015}],
      +            'asks': [{'price': 911.122, 'id': 44647, 'amount': 0.001233, 'timestamp': 1485777124.415542}]
      +        }
      +        """
      +
      +        return self._query("get", f"v1/orderbook/{pair}/", params=kwargs)
      +
      +    def get_charts(self, pair, type, **kwargs):
      +        """Return data for chart candles
      +        :param pair: Pair name
      +        :param type: Type of candles ('1','5','15','30','60','240','D')
      +        :param kwargs: since, until, limit
      +        :return: array of bars
      +        Example [
      +            {'volume': 0.262929, 'high': 912.236, 'low': 910.086, 'close': 911.915, 'time': 1485777600, 'open': 910.424},
      +            {'volume': 2.16483814, 'high': 911.519, 'low': 909.432, 'close': 910.424, 'time': 1485774000, 'open': 909.433}
      +        ]
      +        """
      +        return self._query("get", f"charts/{pair}/{type}/chart", params=kwargs)
      +
      +    def _query(self, method, path, params=None, data=None, auth=False):
      +        url = f"{self._url}{path}"
      +        headers = data and self._get_headers(data)
      +        response = requests.request(method=method, url=url, params=params, data=data, auth=auth, headers=headers)
      +
      +        if 300 > response.status_code >= 200:
      +            return response.json()
      +        else:
      +            raise j.exceptions.Value(f"Http status {response.status_code} - {response.content}")
      +
      +    def _get_headers(self, data):
      +        msg = self.token_id + urlencode(sorted(data.items(), key=lambda val: val[0]))
      +
      +        sign = hmac.new(self.token_secret.encode(), msg.encode(), digestmod="sha256").hexdigest()
      +
      +        return {"X-KEY": self.token_id, "X-SIGN": sign, "X-NONCE": str(int(time() * 1000))}
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class BTCAlpha +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class BTCAlpha(Client):
      +    _url = fields.String(default="https://btc-alpha.com/api/")
      +    token_id = fields.String(default="")
      +    token_secret = fields.Secret(default="")
      +
      +    def get_currencies(self):
      +        """Returns all active currencies
      +        :return: array of currencies
      +        Example: [{'sign': 'Ƀ', 'short_name': 'BTC'}, {'sign': 'Ξ', 'short_name': 'ETH'}, ...]
      +        """
      +        return self._query("get", "v1/currencies/")
      +
      +    def get_pairs(self, **kwargs):
      +        """Returns all active pairs
      +        :param kwargs: Filters (Optional): currency1, currency2
      +        :return: pairs array
      +        Example: [{'currency2': 'USD', 'maximum_order_size': '100000000.00000000', 'minimum_order_size': '0.00000001',
      +                'currency1': 'BTC', 'name': 'BTC_USD', 'price_precision': 3}, ... ]
      +        """
      +        return self._query("get", "v1/pairs/", params=kwargs)
      +
      +    def get_wallets(self, **kwargs):
      +        """Returns own wallets
      +        :param kwargs: Filters (Optional): currency_id
      +        :return: wallets array
      +        Example: [{'currency': 'BTC', 'balance': '0.00000000', 'reserve': '0.00000000'}, ...]
      +        """
      +        return self._query("get", "v1/wallets/", params=kwargs, auth=True)
      +
      +    def get_own_sell_orders(self, **kwargs):
      +        """ Returns own sell orders """
      +        kwargs["type"] = "sell"
      +        return self._query("get", "v1/orders/own/", params=kwargs, auth=True)
      +
      +    def get_own_buy_orders(self, **kwargs):
      +        """ Returns own buy orders """
      +        kwargs["type"] = "buy"
      +        return self._query("get", "v1/orders/own/", params=kwargs, auth=True)
      +
      +    def create_sell_order(self, pair, amount, price):
      +        """Create sell order
      +        :param pair: Pair (BTC_USD,)
      +        :param amount: Amount of order - string
      +        :param price: Price of order - string
      +        :return: order info
      +        Example: {
      +        'type': 'buy', 'date': 1483721079.51632, 'oid': '11268', 'price': '870.69000000', 'amount': '0.00000000',
      +        'trades': [{'type': 'sell', 'price': '870.69000000', 'o_id': '11266', 'amount': '0.00010000', 'tid': '6049'}]
      +        }
      +        """
      +        data = {"pair": pair, "amount": amount, "price": price, "type": "sell"}
      +        return self._query("post", "v1/order/", data=data, auth=True)
      +
      +    def create_buy_order(self, pair, amount, price):
      +        """ Create buy order """
      +        data = {"pair": pair, "amount": amount, "price": price, "type": "buy"}
      +        return self._query("post", "v1/order/", data=data, auth=True)
      +
      +    def cancel_order(self, oid):
      +        """Cancel order
      +        :param oid: id of order
      +        Example: {
      +            order: 63568
      +        }
      +        """
      +        data = {"order": oid}
      +        return self._query("post", "v1/order-cancel/", data=data, auth=True)
      +
      +    def get_exchanges(self, **kwargs):
      +        """Returns last exchanges
      +        :param kwargs: Filters (Optional): pair, limit
      +        :return: exchanges array
      +        Example: [{'id': 6030, 'price': '839.36000000', 'pair': 'BTC_USD', 'type': 1, 'timestamp': 1483705817.735508,
      +                'amount': '0.00281167'}, ....]
      +        """
      +        return self._query("get", "v1/exchanges/", params=kwargs)
      +
      +    def get_own_exchanges(self, **kwargs):
      +        """Returns only own exchanges
      +        :param kwargs: see get_exchanges()
      +        :return: see get_exchanges()
      +        """
      +        return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +    def get_own_sell_exchanges(self, **kwargs):
      +        kwargs["type"] = "sell"
      +        return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +    def get_own_buy_exchanges(self, **kwargs):
      +        kwargs["type"] = "buy"
      +        return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +    def get_deposits(self):
      +        """Returns all made deposits
      +        :return: deposits array
      +        Example [{'timestamp': 1485363039.18359, 'id': 317, 'currency': 'BTC', 'amount': '530.00000000'}, ... ]
      +        """
      +        return self._query("get", "v1/deposits/", auth=True)
      +
      +    def get_withdraws(self, **kwargs):
      +        """Returns all made withdraws
      +        :param kwargs: currency_id, status
      +        :return: withdraws array
      +        Example: [{'id': 403, 'timestamp': 1485363466.868539, 'currency': 'BTC', 'amount': '0.53000000', 'status': 20} ...]
      +        """
      +
      +        return self._query("get", "v1/withdraws/", params=kwargs, auth=True)
      +
      +    def get_orderbook(self, pair, **kwargs):
      +        """Return sell and buy orders for specified pair
      +        :param kwargs: limit_bids, limit_asks, group
      +        :return: object {'bids': Array, 'asks': Array}
      +        Example: {
      +            'bids': [{'price': 911.519, 'id': 44667, 'amount': 0.000446, 'timestamp': 1485777324.410015}],
      +            'asks': [{'price': 911.122, 'id': 44647, 'amount': 0.001233, 'timestamp': 1485777124.415542}]
      +        }
      +        """
      +
      +        return self._query("get", f"v1/orderbook/{pair}/", params=kwargs)
      +
      +    def get_charts(self, pair, type, **kwargs):
      +        """Return data for chart candles
      +        :param pair: Pair name
      +        :param type: Type of candles ('1','5','15','30','60','240','D')
      +        :param kwargs: since, until, limit
      +        :return: array of bars
      +        Example [
      +            {'volume': 0.262929, 'high': 912.236, 'low': 910.086, 'close': 911.915, 'time': 1485777600, 'open': 910.424},
      +            {'volume': 2.16483814, 'high': 911.519, 'low': 909.432, 'close': 910.424, 'time': 1485774000, 'open': 909.433}
      +        ]
      +        """
      +        return self._query("get", f"charts/{pair}/{type}/chart", params=kwargs)
      +
      +    def _query(self, method, path, params=None, data=None, auth=False):
      +        url = f"{self._url}{path}"
      +        headers = data and self._get_headers(data)
      +        response = requests.request(method=method, url=url, params=params, data=data, auth=auth, headers=headers)
      +
      +        if 300 > response.status_code >= 200:
      +            return response.json()
      +        else:
      +            raise j.exceptions.Value(f"Http status {response.status_code} - {response.content}")
      +
      +    def _get_headers(self, data):
      +        msg = self.token_id + urlencode(sorted(data.items(), key=lambda val: val[0]))
      +
      +        sign = hmac.new(self.token_secret.encode(), msg.encode(), digestmod="sha256").hexdigest()
      +
      +        return {"X-KEY": self.token_id, "X-SIGN": sign, "X-NONCE": str(int(time() * 1000))}
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var token_id
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var token_secret
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def cancel_order(self, oid) +
      +
      +

      Cancel order +:param oid: id of order +Example: { +order: 63568 +}

      +
      + +Expand source code + +
      def cancel_order(self, oid):
      +    """Cancel order
      +    :param oid: id of order
      +    Example: {
      +        order: 63568
      +    }
      +    """
      +    data = {"order": oid}
      +    return self._query("post", "v1/order-cancel/", data=data, auth=True)
      +
      +
      +
      +def create_buy_order(self, pair, amount, price) +
      +
      +

      Create buy order

      +
      + +Expand source code + +
      def create_buy_order(self, pair, amount, price):
      +    """ Create buy order """
      +    data = {"pair": pair, "amount": amount, "price": price, "type": "buy"}
      +    return self._query("post", "v1/order/", data=data, auth=True)
      +
      +
      +
      +def create_sell_order(self, pair, amount, price) +
      +
      +

      Create sell order +:param pair: Pair (BTC_USD,) +:param amount: Amount of order - string +:param price: Price of order - string +:return: order info +Example: { +'type': 'buy', 'date': 1483721079.51632, 'oid': '11268', 'price': '870.69000000', 'amount': '0.00000000', +'trades': [{'type': 'sell', 'price': '870.69000000', 'o_id': '11266', 'amount': '0.00010000', 'tid': '6049'}] +}

      +
      + +Expand source code + +
      def create_sell_order(self, pair, amount, price):
      +    """Create sell order
      +    :param pair: Pair (BTC_USD,)
      +    :param amount: Amount of order - string
      +    :param price: Price of order - string
      +    :return: order info
      +    Example: {
      +    'type': 'buy', 'date': 1483721079.51632, 'oid': '11268', 'price': '870.69000000', 'amount': '0.00000000',
      +    'trades': [{'type': 'sell', 'price': '870.69000000', 'o_id': '11266', 'amount': '0.00010000', 'tid': '6049'}]
      +    }
      +    """
      +    data = {"pair": pair, "amount": amount, "price": price, "type": "sell"}
      +    return self._query("post", "v1/order/", data=data, auth=True)
      +
      +
      +
      +def get_charts(self, pair, type, **kwargs) +
      +
      +

      Return data for chart candles +:param pair: Pair name +:param type: Type of candles ('1','5','15','30','60','240','D') +:param kwargs: since, until, limit +:return: array of bars +Example [ +{'volume': 0.262929, 'high': 912.236, 'low': 910.086, 'close': 911.915, 'time': 1485777600, 'open': 910.424}, +{'volume': 2.16483814, 'high': 911.519, 'low': 909.432, 'close': 910.424, 'time': 1485774000, 'open': 909.433} +]

      +
      + +Expand source code + +
      def get_charts(self, pair, type, **kwargs):
      +    """Return data for chart candles
      +    :param pair: Pair name
      +    :param type: Type of candles ('1','5','15','30','60','240','D')
      +    :param kwargs: since, until, limit
      +    :return: array of bars
      +    Example [
      +        {'volume': 0.262929, 'high': 912.236, 'low': 910.086, 'close': 911.915, 'time': 1485777600, 'open': 910.424},
      +        {'volume': 2.16483814, 'high': 911.519, 'low': 909.432, 'close': 910.424, 'time': 1485774000, 'open': 909.433}
      +    ]
      +    """
      +    return self._query("get", f"charts/{pair}/{type}/chart", params=kwargs)
      +
      +
      +
      +def get_currencies(self) +
      +
      +

      Returns all active currencies +:return: array of currencies +Example: [{'sign': 'Ƀ', 'short_name': 'BTC'}, {'sign': 'Ξ', 'short_name': 'ETH'}, …]

      +
      + +Expand source code + +
      def get_currencies(self):
      +    """Returns all active currencies
      +    :return: array of currencies
      +    Example: [{'sign': 'Ƀ', 'short_name': 'BTC'}, {'sign': 'Ξ', 'short_name': 'ETH'}, ...]
      +    """
      +    return self._query("get", "v1/currencies/")
      +
      +
      +
      +def get_deposits(self) +
      +
      +

      Returns all made deposits +:return: deposits array +Example [{'timestamp': 1485363039.18359, 'id': 317, 'currency': 'BTC', 'amount': '530.00000000'}, … ]

      +
      + +Expand source code + +
      def get_deposits(self):
      +    """Returns all made deposits
      +    :return: deposits array
      +    Example [{'timestamp': 1485363039.18359, 'id': 317, 'currency': 'BTC', 'amount': '530.00000000'}, ... ]
      +    """
      +    return self._query("get", "v1/deposits/", auth=True)
      +
      +
      +
      +def get_exchanges(self, **kwargs) +
      +
      +

      Returns last exchanges +:param kwargs: Filters (Optional): pair, limit +:return: exchanges array +Example: [{'id': 6030, 'price': '839.36000000', 'pair': 'BTC_USD', 'type': 1, 'timestamp': 1483705817.735508, +'amount': '0.00281167'}, ....]

      +
      + +Expand source code + +
      def get_exchanges(self, **kwargs):
      +    """Returns last exchanges
      +    :param kwargs: Filters (Optional): pair, limit
      +    :return: exchanges array
      +    Example: [{'id': 6030, 'price': '839.36000000', 'pair': 'BTC_USD', 'type': 1, 'timestamp': 1483705817.735508,
      +            'amount': '0.00281167'}, ....]
      +    """
      +    return self._query("get", "v1/exchanges/", params=kwargs)
      +
      +
      +
      +def get_orderbook(self, pair, **kwargs) +
      +
      +

      Return sell and buy orders for specified pair +:param kwargs: limit_bids, limit_asks, group +:return: object {'bids': Array, 'asks': Array} +Example: { +'bids': [{'price': 911.519, 'id': 44667, 'amount': 0.000446, 'timestamp': 1485777324.410015}], +'asks': [{'price': 911.122, 'id': 44647, 'amount': 0.001233, 'timestamp': 1485777124.415542}] +}

      +
      + +Expand source code + +
      def get_orderbook(self, pair, **kwargs):
      +    """Return sell and buy orders for specified pair
      +    :param kwargs: limit_bids, limit_asks, group
      +    :return: object {'bids': Array, 'asks': Array}
      +    Example: {
      +        'bids': [{'price': 911.519, 'id': 44667, 'amount': 0.000446, 'timestamp': 1485777324.410015}],
      +        'asks': [{'price': 911.122, 'id': 44647, 'amount': 0.001233, 'timestamp': 1485777124.415542}]
      +    }
      +    """
      +
      +    return self._query("get", f"v1/orderbook/{pair}/", params=kwargs)
      +
      +
      +
      +def get_own_buy_exchanges(self, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def get_own_buy_exchanges(self, **kwargs):
      +    kwargs["type"] = "buy"
      +    return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +
      +
      +def get_own_buy_orders(self, **kwargs) +
      +
      +

      Returns own buy orders

      +
      + +Expand source code + +
      def get_own_buy_orders(self, **kwargs):
      +    """ Returns own buy orders """
      +    kwargs["type"] = "buy"
      +    return self._query("get", "v1/orders/own/", params=kwargs, auth=True)
      +
      +
      +
      +def get_own_exchanges(self, **kwargs) +
      +
      +

      Returns only own exchanges +:param kwargs: see get_exchanges() +:return: see get_exchanges()

      +
      + +Expand source code + +
      def get_own_exchanges(self, **kwargs):
      +    """Returns only own exchanges
      +    :param kwargs: see get_exchanges()
      +    :return: see get_exchanges()
      +    """
      +    return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +
      +
      +def get_own_sell_exchanges(self, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def get_own_sell_exchanges(self, **kwargs):
      +    kwargs["type"] = "sell"
      +    return self._query("get", "v1/exchanges/own/", params=kwargs, auth=True)
      +
      +
      +
      +def get_own_sell_orders(self, **kwargs) +
      +
      +

      Returns own sell orders

      +
      + +Expand source code + +
      def get_own_sell_orders(self, **kwargs):
      +    """ Returns own sell orders """
      +    kwargs["type"] = "sell"
      +    return self._query("get", "v1/orders/own/", params=kwargs, auth=True)
      +
      +
      +
      +def get_pairs(self, **kwargs) +
      +
      +

      Returns all active pairs +:param kwargs: Filters (Optional): currency1, currency2 +:return: pairs array +Example: [{'currency2': 'USD', 'maximum_order_size': '100000000.00000000', 'minimum_order_size': '0.00000001', +'currency1': 'BTC', 'name': 'BTC_USD', 'price_precision': 3}, … ]

      +
      + +Expand source code + +
      def get_pairs(self, **kwargs):
      +    """Returns all active pairs
      +    :param kwargs: Filters (Optional): currency1, currency2
      +    :return: pairs array
      +    Example: [{'currency2': 'USD', 'maximum_order_size': '100000000.00000000', 'minimum_order_size': '0.00000001',
      +            'currency1': 'BTC', 'name': 'BTC_USD', 'price_precision': 3}, ... ]
      +    """
      +    return self._query("get", "v1/pairs/", params=kwargs)
      +
      +
      +
      +def get_wallets(self, **kwargs) +
      +
      +

      Returns own wallets +:param kwargs: Filters (Optional): currency_id +:return: wallets array +Example: [{'currency': 'BTC', 'balance': '0.00000000', 'reserve': '0.00000000'}, …]

      +
      + +Expand source code + +
      def get_wallets(self, **kwargs):
      +    """Returns own wallets
      +    :param kwargs: Filters (Optional): currency_id
      +    :return: wallets array
      +    Example: [{'currency': 'BTC', 'balance': '0.00000000', 'reserve': '0.00000000'}, ...]
      +    """
      +    return self._query("get", "v1/wallets/", params=kwargs, auth=True)
      +
      +
      +
      +def get_withdraws(self, **kwargs) +
      +
      +

      Returns all made withdraws +:param kwargs: currency_id, status +:return: withdraws array +Example: [{'id': 403, 'timestamp': 1485363466.868539, 'currency': 'BTC', 'amount': '0.53000000', 'status': 20} …]

      +
      + +Expand source code + +
      def get_withdraws(self, **kwargs):
      +    """Returns all made withdraws
      +    :param kwargs: currency_id, status
      +    :return: withdraws array
      +    Example: [{'id': 403, 'timestamp': 1485363466.868539, 'currency': 'BTC', 'amount': '0.53000000', 'status': 20} ...]
      +    """
      +
      +    return self._query("get", "v1/withdraws/", params=kwargs, auth=True)
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/btc_alpha/index.html b/docs/api/jumpscale/clients/btc_alpha/index.html new file mode 100644 index 000000000..1d0e45c4c --- /dev/null +++ b/docs/api/jumpscale/clients/btc_alpha/index.html @@ -0,0 +1,102 @@ + + + + + + +jumpscale.clients.btc_alpha API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.btc_alpha

      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import StoredFactory
      +
      +    from .btc_alpha import BTCAlpha
      +
      +    return StoredFactory(BTCAlpha)
      +
      +
      +
      +

      Sub-modules

      +
      +
      jumpscale.clients.btc_alpha.btc_alpha
      +
      +

      BTC_ALPHA client +interacts with btc-alpha.com to provides trading operations and broadcasting of all trading events …

      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import StoredFactory
      +
      +    from .btc_alpha import BTCAlpha
      +
      +    return StoredFactory(BTCAlpha)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/currencylayer/currencies.html b/docs/api/jumpscale/clients/currencylayer/currencies.html new file mode 100644 index 000000000..6b4da6050 --- /dev/null +++ b/docs/api/jumpscale/clients/currencylayer/currencies.html @@ -0,0 +1,234 @@ + + + + + + +jumpscale.clients.currencylayer.currencies API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.currencylayer.currencies

      +
      +
      +
      + +Expand source code + +
      CURRENCIES = {
      +    "aed": 3.672798,
      +    "afn": 71.150002,
      +    "all": 104.900002,
      +    "amd": 482.799988,
      +    "ang": 1.789542,
      +    "aoa": 238.544006,
      +    "ars": 24.896023,
      +    "aud": 1.306702,
      +    "awg": 1.78,
      +    "azn": 1.699505,
      +    "bam": 1.656031,
      +    "bbd": 2,
      +    "bdt": 84.099998,
      +    "bgn": 1.6624,
      +    "bhd": 0.377503,
      +    "bif": 1750.97998,
      +    "bmd": 1,
      +    "bnd": 1.321296,
      +    "bob": 6.859653,
      +    "brl": 3.846501,
      +    "bsd": 1,
      +    "btc": 0.00013,
      +    "btn": 66.925003,
      +    "bwp": 9.995016,
      +    "byn": 2.000042,
      +    "byr": 19600,
      +    "bzd": 1.997802,
      +    "cad": 1.29528,
      +    "cdf": 1565.499204,
      +    "chf": 0.98171,
      +    "clf": 0.02292,
      +    "clp": 627.700012,
      +    "cny": 6.392502,
      +    "cop": 2828,
      +    "crc": 564.749842,
      +    "cuc": 1,
      +    "cup": 26.5,
      +    "cve": 93.239998,
      +    "czk": 21.658998,
      +    "djf": 177.498647,
      +    "dkk": 6.29685,
      +    "dop": 49.519768,
      +    "dzd": 115.929789,
      +    "egp": 17.809999,
      +    "ern": 14.989959,
      +    "etb": 27.209999,
      +    "eth": 607.56,
      +    "eur": 0.845101,
      +    "fjd": 2.052503,
      +    "fkp": 0.744398,
      +    "gbp": 0.74283,
      +    "gel": 2.436399,
      +    "ggp": 0.74271,
      +    "ghs": 4.708496,
      +    "gip": 0.744601,
      +    "gmd": 46.849998,
      +    "gnf": 9005.00012,
      +    "gtq": 7.336024,
      +    "gyd": 207.479996,
      +    "hkd": 7.84544,
      +    "hnl": 23.865999,
      +    "hrk": 6.239802,
      +    "htg": 63.009998,
      +    "huf": 267.98999,
      +    "idr": 13863,
      +    "ils": 3.570702,
      +    "imp": 0.74271,
      +    "inr": 67.037498,
      +    "iqd": 1184,
      +    "irr": 42189.999401,
      +    "isk": 105.250285,
      +    "jep": 0.74271,
      +    "jmd": 127.459999,
      +    "jod": 0.709596,
      +    "jpy": 109.914001,
      +    "kes": 100.599998,
      +    "kgs": 68.425003,
      +    "khr": 4070.000031,
      +    "kmf": 416.950012,
      +    "kpw": 900.000395,
      +    "krw": 1067.319946,
      +    "kwd": 0.301798,
      +    "kyd": 0.819835,
      +    "kzt": 333.399994,
      +    "lak": 8340.000397,
      +    "lbp": 1504.999817,
      +    "lkr": 158.699997,
      +    "lrd": 138.270004,
      +    "lsl": 12.710279,
      +    "ltl": 3.0487,
      +    "lvl": 0.62055,
      +    "lyd": 1.358105,
      +    "mad": 9.413802,
      +    "mdl": 16.724953,
      +    "mga": 3270.000207,
      +    "mkd": 51.770006,
      +    "mmk": 1352.000157,
      +    "mnt": 2406.000152,
      +    "mop": 8.080397,
      +    "mro": 354.000075,
      +    "mur": 33.849998,
      +    "mvr": 15.569619,
      +    "mwk": 717.359985,
      +    "mxn": 20.368401,
      +    "myr": 3.974993,
      +    "mzn": 58.820231,
      +    "nad": 12.716026,
      +    "ngn": 357.999747,
      +    "nio": 31.447599,
      +    "nok": 8.02761,
      +    "npr": 107.099998,
      +    "nzd": 1.418004,
      +    "omr": 0.384801,
      +    "pab": 1,
      +    "pen": 3.260601,
      +    "pgk": 3.260104,
      +    "php": 52.450001,
      +    "pkr": 115.599998,
      +    "pln": 3.603797,
      +    "pyg": 5668.399902,
      +    "qar": 3.639797,
      +    "ron": 3.9354,
      +    "rsd": 99.820999,
      +    "rub": 61.893799,
      +    "rwf": 848.200012,
      +    "sar": 3.749802,
      +    "sbd": 7.933964,
      +    "scr": 13.429735,
      +    "sdg": 17.955099,
      +    "sek": 8.65494,
      +    "sgd": 1.33102,
      +    "shp": 0.744596,
      +    "sll": 7849.99989,
      +    "sos": 562.999736,
      +    "srd": 7.419726,
      +    "std": 20718.800781,
      +    "svc": 8.750051,
      +    "syp": 514.97998,
      +    "szl": 12.716961,
      +    "thb": 31.909815,
      +    "tjs": 9.049018,
      +    "tmt": 3.41,
      +    "tnd": 2.5476,
      +    "top": 2.2843,
      +    "try": 4.562502,
      +    "ttd": 6.649497,
      +    "twd": 29.684036,
      +    "tzs": 2268.000038,
      +    "uah": 26.163009,
      +    "ugx": 3801.999988,
      +    "usd": 1,
      +    "uyu": 31.079742,
      +    "uzs": 7924.999809,
      +    "vef": 79800.000475,
      +    "vnd": 22800,
      +    "vuv": 106.209999,
      +    "wst": 2.562898,
      +    "xaf": 554.150024,
      +    "xag": 0.059753,
      +    "xau": 0.00077,
      +    "xcd": 2.698788,
      +    "xdr": 0.704103,
      +    "xof": 554.150024,
      +    "xpf": 100.903624,
      +    "xrp": 0.6805,
      +    "yer": 249.929993,
      +    "zar": 12.723501,
      +    "zmk": 9001.212517,
      +    "zmw": 10.080297,
      +    "zwl": 322.355011,
      +}
      +
      +
      +CURRNECIES_IDS = dict(zip(range(1, len(CURRENCIES)+1), CURRENCIES.keys()))
      +IDS_CURRENCIES = dict(zip(CURRENCIES.keys(), range(1, len(CURRENCIES)+1)))
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/currencylayer/currencylayer.html b/docs/api/jumpscale/clients/currencylayer/currencylayer.html new file mode 100644 index 000000000..7fb1631bf --- /dev/null +++ b/docs/api/jumpscale/clients/currencylayer/currencylayer.html @@ -0,0 +1,508 @@ + + + + + + +jumpscale.clients.currencylayer.currencylayer API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.currencylayer.currencylayer

      +
      +
      +
      + +Expand source code + +
      from pprint import pprint as print
      +import cryptocompare as cc
      +from jumpscale.clients.base import Client
      +from jumpscale.core.base import fields
      +from jumpscale.loader import j
      +from .currencies import CURRENCIES, CURRNECIES_IDS
      +from pprint import pprint
      +
      +
      +def get_currency_data(api_key, fake=False, fakeonerror=False):
      +    if fake:
      +        return CURRENCIES
      +    else:
      +        url = "http://apilayer.net/api/live?access_key={}".format(api_key)
      +        r = j.tools.http.get(url)
      +        try:
      +            json_res = r.json()
      +            data = json_res["quotes"]
      +
      +            data["USDETH"] = 1 / cc.get_price("ETH", "USD")["ETH"]["USD"]
      +            data["USDXRP"] = cc.get_price("USD", "XRP")["USD"]["XRP"]
      +            data["USDBTC"] = 1 / cc.get_price("BTC", "USD")["BTC"]["USD"]
      +
      +            normalized_data = {k.lower().lstrip("usd"): v for k, v in data.items()}
      +            return normalized_data
      +
      +        except Exception as e:
      +            print("error happened")
      +            if not fakeonerror:
      +                raise e
      +            else:
      +                return CURRENCIES
      +
      +
      +class CurrencyLayerClient(Client):
      +
      +    name = fields.String()
      +    api_key = fields.String()
      +    fake = fields.Boolean(default=True)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self.__client = None
      +
      +        self._data_cur = {}
      +        self._id2cur = {}
      +        self._cur2id = {}
      +
      +    def load(self):
      +        # data = self._cache.get("currency_data", get, expire=3600 * 24)
      +        data = get_currency_data(self.api_key, fake=self.fake)
      +        self._data_cur = data
      +
      +    @property
      +    def cur2usd(self):
      +        """
      +        e.g. AED = 3,672 means 3,6... times AED=1 USD
      +        """
      +        if not self._data_cur:
      +            self.load()
      +        return self._data_cur
      +
      +    def cur2usd_print(self):
      +        print(self.cur2usd)
      +
      +    @property
      +    def id2cur(self):
      +        if not self._id2cur:
      +            self._id2cur = CURRNECIES_IDS
      +        return self._id2cur
      +
      +    @property
      +    def cur2id(self):
      +        if not self._cur2id:
      +            self._cur2id = dict(zip(self.id2cur.values(), self.id2cur.keys()))
      +        return self._cur2id
      +
      +    def id2cur_print(self):
      +        pprint(self.id2cur)
      +
      +    def cur2id_print(self):
      +        pprint(self.cur2id)
      +
      +    def test(self):
      +        self._log_info(self.cur2usd)
      +        assert "aed" in self.cur2usd
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def get_currency_data(api_key, fake=False, fakeonerror=False) +
      +
      +
      +
      + +Expand source code + +
      def get_currency_data(api_key, fake=False, fakeonerror=False):
      +    if fake:
      +        return CURRENCIES
      +    else:
      +        url = "http://apilayer.net/api/live?access_key={}".format(api_key)
      +        r = j.tools.http.get(url)
      +        try:
      +            json_res = r.json()
      +            data = json_res["quotes"]
      +
      +            data["USDETH"] = 1 / cc.get_price("ETH", "USD")["ETH"]["USD"]
      +            data["USDXRP"] = cc.get_price("USD", "XRP")["USD"]["XRP"]
      +            data["USDBTC"] = 1 / cc.get_price("BTC", "USD")["BTC"]["USD"]
      +
      +            normalized_data = {k.lower().lstrip("usd"): v for k, v in data.items()}
      +            return normalized_data
      +
      +        except Exception as e:
      +            print("error happened")
      +            if not fakeonerror:
      +                raise e
      +            else:
      +                return CURRENCIES
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class CurrencyLayerClient +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class CurrencyLayerClient(Client):
      +
      +    name = fields.String()
      +    api_key = fields.String()
      +    fake = fields.Boolean(default=True)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self.__client = None
      +
      +        self._data_cur = {}
      +        self._id2cur = {}
      +        self._cur2id = {}
      +
      +    def load(self):
      +        # data = self._cache.get("currency_data", get, expire=3600 * 24)
      +        data = get_currency_data(self.api_key, fake=self.fake)
      +        self._data_cur = data
      +
      +    @property
      +    def cur2usd(self):
      +        """
      +        e.g. AED = 3,672 means 3,6... times AED=1 USD
      +        """
      +        if not self._data_cur:
      +            self.load()
      +        return self._data_cur
      +
      +    def cur2usd_print(self):
      +        print(self.cur2usd)
      +
      +    @property
      +    def id2cur(self):
      +        if not self._id2cur:
      +            self._id2cur = CURRNECIES_IDS
      +        return self._id2cur
      +
      +    @property
      +    def cur2id(self):
      +        if not self._cur2id:
      +            self._cur2id = dict(zip(self.id2cur.values(), self.id2cur.keys()))
      +        return self._cur2id
      +
      +    def id2cur_print(self):
      +        pprint(self.id2cur)
      +
      +    def cur2id_print(self):
      +        pprint(self.cur2id)
      +
      +    def test(self):
      +        self._log_info(self.cur2usd)
      +        assert "aed" in self.cur2usd
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var api_key
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var cur2id
      +
      +
      +
      + +Expand source code + +
      @property
      +def cur2id(self):
      +    if not self._cur2id:
      +        self._cur2id = dict(zip(self.id2cur.values(), self.id2cur.keys()))
      +    return self._cur2id
      +
      +
      +
      var cur2usd
      +
      +

      e.g. AED = 3,672 means 3,6… times AED=1 USD

      +
      + +Expand source code + +
      @property
      +def cur2usd(self):
      +    """
      +    e.g. AED = 3,672 means 3,6... times AED=1 USD
      +    """
      +    if not self._data_cur:
      +        self.load()
      +    return self._data_cur
      +
      +
      +
      var fake
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var id2cur
      +
      +
      +
      + +Expand source code + +
      @property
      +def id2cur(self):
      +    if not self._id2cur:
      +        self._id2cur = CURRNECIES_IDS
      +    return self._id2cur
      +
      +
      +
      var name
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def cur2id_print(self) +
      +
      +
      +
      + +Expand source code + +
      def cur2id_print(self):
      +    pprint(self.cur2id)
      +
      +
      +
      +def cur2usd_print(self) +
      +
      +
      +
      + +Expand source code + +
      def cur2usd_print(self):
      +    print(self.cur2usd)
      +
      +
      +
      +def id2cur_print(self) +
      +
      +
      +
      + +Expand source code + +
      def id2cur_print(self):
      +    pprint(self.id2cur)
      +
      +
      +
      +def load(self) +
      +
      +
      +
      + +Expand source code + +
      def load(self):
      +    # data = self._cache.get("currency_data", get, expire=3600 * 24)
      +    data = get_currency_data(self.api_key, fake=self.fake)
      +    self._data_cur = data
      +
      +
      +
      +def test(self) +
      +
      +
      +
      + +Expand source code + +
      def test(self):
      +    self._log_info(self.cur2usd)
      +    assert "aed" in self.cur2usd
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/currencylayer/index.html b/docs/api/jumpscale/clients/currencylayer/index.html new file mode 100644 index 000000000..f416b6957 --- /dev/null +++ b/docs/api/jumpscale/clients/currencylayer/index.html @@ -0,0 +1,1523 @@ + + + + + + +jumpscale.clients.currencylayer API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.currencylayer

      +
      +
      +
      +
      JS-NG> fake = j.clients.currencylayer.new('fake')
      +
      JS-NG> fake.cur2id_print()
      +
      {'aed': 1,
      +
      'afn': 2,
      +
      'all': 3,
      +
      'amd': 4,
      +
      'ang': 5,
      +
      'aoa': 6,
      +
      'ars': 7,
      +
      'aud': 8,
      +
      'awg': 9,
      +
      'azn': 10,
      +
      'bam': 11,
      +
      'bbd': 12,
      +
      'bdt': 13,
      +
      'bgn': 14,
      +
      'bhd': 15,
      +
      'bif': 16,
      +
      'bmd': 17,
      +
      'bnd': 18,
      +
      'bob': 19,
      +
      'brl': 20,
      +
      'bsd': 21,
      +
      'btc': 22,
      +
      'btn': 23,
      +
      'bwp': 24,
      +
      'byn': 25,
      +
      'byr': 26,
      +
      'bzd': 27,
      +
      'cad': 28,
      +
      'cdf': 29,
      +
      'chf': 30,
      +
      'clf': 31,
      +
      'clp': 32,
      +
      'cny': 33,
      +
      'cop': 34,
      +
      'crc': 35,
      +
      'cuc': 36,
      +
      'cup': 37,
      +
      'cve': 38,
      +
      'czk': 39,
      +
      'djf': 40,
      +
      'dkk': 41,
      +
      'dop': 42,
      +
      'dzd': 43,
      +
      'egp': 44,
      +
      'ern': 45,
      +
      'etb': 46,
      +
      'eth': 47,
      +
      'eur': 48,
      +
      'fjd': 49,
      +
      'fkp': 50,
      +
      'gbp': 51,
      +
      'gel': 52,
      +
      'ggp': 53,
      +
      'ghs': 54,
      +
      'gip': 55,
      +
      'gmd': 56,
      +
      'gnf': 57,
      +
      'gtq': 58,
      +
      'gyd': 59,
      +
      'hkd': 60,
      +
      'hnl': 61,
      +
      'hrk': 62,
      +
      'htg': 63,
      +
      'huf': 64,
      +
      'idr': 65,
      +
      'ils': 66,
      +
      'imp': 67,
      +
      'inr': 68,
      +
      'iqd': 69,
      +
      'irr': 70,
      +
      'isk': 71,
      +
      'jep': 72,
      +
      'jmd': 73,
      +
      'jod': 74,
      +
      'jpy': 75,
      +
      'kes': 76,
      +
      'kgs': 77,
      +
      'khr': 78,
      +
      'kmf': 79,
      +
      'kpw': 80,
      +
      'krw': 81,
      +
      'kwd': 82,
      +
      'kyd': 83,
      +
      'kzt': 84,
      +
      'lak': 85,
      +
      'lbp': 86,
      +
      'lkr': 87,
      +
      'lrd': 88,
      +
      'lsl': 89,
      +
      'ltl': 90,
      +
      'lvl': 91,
      +
      'lyd': 92,
      +
      'mad': 93,
      +
      'mdl': 94,
      +
      'mga': 95,
      +
      'mkd': 96,
      +
      'mmk': 97,
      +
      'mnt': 98,
      +
      'mop': 99,
      +
      'mro': 100,
      +
      'mur': 101,
      +
      'mvr': 102,
      +
      'mwk': 103,
      +
      'mxn': 104,
      +
      'myr': 105,
      +
      'mzn': 106,
      +
      'nad': 107,
      +
      'ngn': 108,
      +
      'nio': 109,
      +
      'nok': 110,
      +
      'npr': 111,
      +
      'nzd': 112,
      +
      'omr': 113,
      +
      'pab': 114,
      +
      'pen': 115,
      +
      'pgk': 116,
      +
      'php': 117,
      +
      'pkr': 118,
      +
      'pln': 119,
      +
      'pyg': 120,
      +
      'qar': 121,
      +
      'ron': 122,
      +
      'rsd': 123,
      +
      'rub': 124,
      +
      'rwf': 125,
      +
      'sar': 126,
      +
      'sbd': 127,
      +
      'scr': 128,
      +
      'sdg': 129,
      +
      'sek': 130,
      +
      'sgd': 131,
      +
      'shp': 132,
      +
      'sll': 133,
      +
      'sos': 134,
      +
      'srd': 135,
      +
      'std': 136,
      +
      'svc': 137,
      +
      'syp': 138,
      +
      'szl': 139,
      +
      'thb': 140,
      +
      'tjs': 141,
      +
      'tmt': 142,
      +
      'tnd': 143,
      +
      'top': 144,
      +
      'try': 145,
      +
      'ttd': 146,
      +
      'twd': 147,
      +
      'tzs': 148,
      +
      'uah': 149,
      +
      'ugx': 150,
      +
      'usd': 151,
      +
      'uyu': 152,
      +
      'uzs': 153,
      +
      'vef': 154,
      +
      'vnd': 155,
      +
      'vuv': 156,
      +
      'wst': 157,
      +
      'xaf': 158,
      +
      'xag': 159,
      +
      'xau': 160,
      +
      'xcd': 161,
      +
      'xdr': 162,
      +
      'xof': 163,
      +
      'xpf': 164,
      +
      'xrp': 165,
      +
      'yer': 166,
      +
      'zar': 167,
      +
      'zmk': 168,
      +
      'zmw': 169,
      +
      'zwl': 170}
      +
      JS-NG> fake.id2cur_print()
      +
      {1: 'aed',
      +
      2: 'afn',
      +
      3: 'all',
      +
      4: 'amd',
      +
      5: 'ang',
      +
      6: 'aoa',
      +
      7: 'ars',
      +
      8: 'aud',
      +
      9: 'awg',
      +
      10: 'azn',
      +
      11: 'bam',
      +
      12: 'bbd',
      +
      13: 'bdt',
      +
      14: 'bgn',
      +
      15: 'bhd',
      +
      16: 'bif',
      +
      17: 'bmd',
      +
      18: 'bnd',
      +
      19: 'bob',
      +
      20: 'brl',
      +
      21: 'bsd',
      +
      22: 'btc',
      +
      23: 'btn',
      +
      24: 'bwp',
      +
      25: 'byn',
      +
      26: 'byr',
      +
      27: 'bzd',
      +
      28: 'cad',
      +
      29: 'cdf',
      +
      30: 'chf',
      +
      31: 'clf',
      +
      32: 'clp',
      +
      33: 'cny',
      +
      34: 'cop',
      +
      35: 'crc',
      +
      36: 'cuc',
      +
      37: 'cup',
      +
      38: 'cve',
      +
      39: 'czk',
      +
      40: 'djf',
      +
      41: 'dkk',
      +
      42: 'dop',
      +
      43: 'dzd',
      +
      44: 'egp',
      +
      45: 'ern',
      +
      46: 'etb',
      +
      47: 'eth',
      +
      48: 'eur',
      +
      49: 'fjd',
      +
      50: 'fkp',
      +
      51: 'gbp',
      +
      52: 'gel',
      +
      53: 'ggp',
      +
      54: 'ghs',
      +
      55: 'gip',
      +
      56: 'gmd',
      +
      57: 'gnf',
      +
      58: 'gtq',
      +
      59: 'gyd',
      +
      60: 'hkd',
      +
      61: 'hnl',
      +
      62: 'hrk',
      +
      63: 'htg',
      +
      64: 'huf',
      +
      65: 'idr',
      +
      66: 'ils',
      +
      67: 'imp',
      +
      68: 'inr',
      +
      69: 'iqd',
      +
      70: 'irr',
      +
      71: 'isk',
      +
      72: 'jep',
      +
      73: 'jmd',
      +
      74: 'jod',
      +
      75: 'jpy',
      +
      76: 'kes',
      +
      77: 'kgs',
      +
      78: 'khr',
      +
      79: 'kmf',
      +
      80: 'kpw',
      +
      81: 'krw',
      +
      82: 'kwd',
      +
      83: 'kyd',
      +
      84: 'kzt',
      +
      85: 'lak',
      +
      86: 'lbp',
      +
      87: 'lkr',
      +
      88: 'lrd',
      +
      89: 'lsl',
      +
      90: 'ltl',
      +
      91: 'lvl',
      +
      92: 'lyd',
      +
      93: 'mad',
      +
      94: 'mdl',
      +
      95: 'mga',
      +
      96: 'mkd',
      +
      97: 'mmk',
      +
      98: 'mnt',
      +
      99: 'mop',
      +
      100: 'mro',
      +
      101: 'mur',
      +
      102: 'mvr',
      +
      103: 'mwk',
      +
      104: 'mxn',
      +
      105: 'myr',
      +
      106: 'mzn',
      +
      107: 'nad',
      +
      108: 'ngn',
      +
      109: 'nio',
      +
      110: 'nok',
      +
      111: 'npr',
      +
      112: 'nzd',
      +
      113: 'omr',
      +
      114: 'pab',
      +
      115: 'pen',
      +
      116: 'pgk',
      +
      117: 'php',
      +
      118: 'pkr',
      +
      119: 'pln',
      +
      120: 'pyg',
      +
      121: 'qar',
      +
      122: 'ron',
      +
      123: 'rsd',
      +
      124: 'rub',
      +
      125: 'rwf',
      +
      126: 'sar',
      +
      127: 'sbd',
      +
      128: 'scr',
      +
      129: 'sdg',
      +
      130: 'sek',
      +
      131: 'sgd',
      +
      132: 'shp',
      +
      133: 'sll',
      +
      134: 'sos',
      +
      135: 'srd',
      +
      136: 'std',
      +
      137: 'svc',
      +
      138: 'syp',
      +
      139: 'szl',
      +
      140: 'thb',
      +
      141: 'tjs',
      +
      142: 'tmt',
      +
      143: 'tnd',
      +
      144: 'top',
      +
      145: 'try',
      +
      146: 'ttd',
      +
      147: 'twd',
      +
      148: 'tzs',
      +
      149: 'uah',
      +
      150: 'ugx',
      +
      151: 'usd',
      +
      152: 'uyu',
      +
      153: 'uzs',
      +
      154: 'vef',
      +
      155: 'vnd',
      +
      156: 'vuv',
      +
      157: 'wst',
      +
      158: 'xaf',
      +
      159: 'xag',
      +
      160: 'xau',
      +
      161: 'xcd',
      +
      162: 'xdr',
      +
      163: 'xof',
      +
      164: 'xpf',
      +
      165: 'xrp',
      +
      166: 'yer',
      +
      167: 'zar',
      +
      168: 'zmk',
      +
      169: 'zmw',
      +
      170: 'zwl'}
      +
      JS-NG> fake.id2cur
      +
      {1: 'aed', 2: 'afn', 3: 'all', 4: 'amd', 5: 'ang', 6: 'aoa', 7: 'ars', 8: 'aud', 9: 'awg', 10: 'azn', 11: 'bam', 12: 'b
      +
      bd', 13: 'bdt', 14: 'bgn', 15: 'bhd', 16: 'bif', 17: 'bmd', 18: 'bnd', 19: 'bob', 20: 'brl', 21: 'bsd', 22: 'btc', 23:
      +
      'btn', 24: 'bwp', 25: 'byn', 26: 'byr', 27: 'bzd', 28: 'cad', 29: 'cdf', 30: 'chf', 31: 'clf', 32: 'clp', 33: 'cny', 34
      +
      'cop', 35: 'crc', 36: 'cuc', 37: 'cup', 38: 'cve', 39: 'czk', 40: 'djf', 41: 'dkk', 42: 'dop', 43: 'dzd', 44: 'egp', +45: 'ern', 46: 'etb', 47: 'eth', 48: 'eur', 49: 'fjd', 50: 'fkp', 51: 'gbp', 52: 'gel', 53: 'ggp', 54: 'ghs', 55: 'gip' +, 56: 'gmd', 57: 'gnf', 58: 'gtq', 59: 'gyd', 60: 'hkd', 61: 'hnl', 62: 'hrk', 63: 'htg', 64: 'huf', 65: 'idr', 66: 'ils', 67: 'imp', 68: 'inr', 69: 'iqd', 70: 'irr', 71: 'isk', 72: 'jep', 73: 'jmd', 74: 'jod', 75: 'jpy', 76: 'kes', 77: 'kgs', 78: 'khr', 79: 'kmf', 80: 'kpw', 81: 'krw', 82: 'kwd', 83: 'kyd', 84: 'kzt', 85: 'lak', 86: 'lbp', 87: 'lkr', 88: 'lrd', 89: 'lsl', 90: 'ltl', 91: 'lvl', 92: 'lyd', 93: 'mad', 94: 'mdl', 95: 'mga', 96: 'mkd', 97: 'mmk', 98: 'mnt', 99: 'mop', 100: 'mro', 101: 'mur', 102: 'mvr', 103: 'mwk', 104: 'mxn', 105: 'myr', 106: 'mzn', 107: 'nad', 108: 'ngn', 109: 'nio', 110: 'nok', 111: 'npr', 112: 'nzd', 113: 'omr', 114: 'pab', 115: 'pen', 116: 'pgk', 117: 'php', 118: 'pkr', 119: 'pln', 120: 'pyg', 121: 'qar', 122: 'ron', 123: 'rsd', 124: 'rub', 125: 'rwf', 126: 'sar', 127: 'sbd', 128: 'scr', 129: 'sdg', 130: 'sek', 131: 'sgd', 132: 'shp', 133: 'sll', 134: 'sos', 135: 'srd', 136: 'std', 137: 'svc', 138: 'syp', 139: 'szl', 140: 'thb', 141: 'tjs', 142: 'tmt', 143: 'tnd', 144: 'top', 145: 'try', 146: 'ttd', 147: 'twd', 148: 'tzs', 149: 'uah', 150: 'ugx', 151: 'usd', 152: 'uyu', 153: 'uzs', 154: 'vef', 155: 'vnd', 156: 'vuv', 157: 'wst', 158: 'xaf', 159: 'xag', 160: 'xau', 161: 'xcd', 162: 'xdr', 163: 'xof', 164: 'xpf', 165: 'xrp', 166: 'yer', 167: 'zar', 168: 'zmk', 169: 'zmw', 170: 'zwl'}
      +
      +

      JS-NG> fake.cur2id +
      +{'aed': 1, 'afn': 2, 'all': 3, 'amd': 4, 'ang': 5, 'aoa': 6, 'ars': 7, 'aud': 8, 'awg': 9, 'azn': 10, 'bam': 11, 'bbd': +12, 'bdt': 13, 'bgn': 14, 'bhd': 15, 'bif': 16, 'bmd': 17, 'bnd': 18, 'bob': 19, 'brl': 20, 'bsd': 21, 'btc': 22, 'btn +': 23, 'bwp': 24, 'byn': 25, 'byr': 26, 'bzd': 27, 'cad': 28, 'cdf': 29, 'chf': 30, 'clf': 31, 'clp': 32, 'cny': 33, 'c +op': 34, 'crc': 35, 'cuc': 36, 'cup': 37, 'cve': 38, 'czk': 39, 'djf': 40, 'dkk': 41, 'dop': 42, 'dzd': 43, 'egp': 44, +'ern': 45, 'etb': 46, 'eth': 47, 'eur': 48, 'fjd': 49, 'fkp': 50, 'gbp': 51, 'gel': 52, 'ggp': 53, 'ghs': 54, 'gip': 55 +, 'gmd': 56, 'gnf': 57, 'gtq': 58, 'gyd': 59, 'hkd': 60, 'hnl': 61, 'hrk': 62, 'htg': 63, 'huf': 64, 'idr': 65, 'ils': 66, 'imp': 67, 'inr': 68, 'iqd': 69, 'irr': 70, 'isk': 71, 'jep': 72, 'jmd': 73, 'jod': 74, 'jpy': 75, 'kes': 76, 'kgs': 77, 'khr': 78, 'kmf': 79, 'kpw': 80, 'krw': 81, 'kwd': 82, 'kyd': 83, 'kzt': 84, 'lak': 85, 'lbp': 86, 'lkr': 87, 'lrd': 88, 'lsl': 89, 'ltl': 90, 'lvl': 91, 'lyd': 92, 'mad': 93, 'mdl': 94, 'mga': 95, 'mkd': 96, 'mmk': 97, 'mnt': 98, 'mop': 99, 'mro': 100, 'mur': 101, 'mvr': 102, 'mwk': 103, 'mxn': 104, 'myr': 105, 'mzn': 106, 'nad': 107, 'ngn': 108, 'nio': 109, 'nok': 110, 'npr': 111, 'nzd': 112, 'omr': 113, 'pab': 114, 'pen': 115, 'pgk': 116, 'php': 117, 'pkr': 118, 'pln': 119, 'pyg': 120, 'qar': 121, 'ron': 122, 'rsd': 123, 'rub': 124, 'rwf': 125, 'sar': 126, 'sbd': 127, 'scr': 128, 'sdg': 129, 'sek': 130, 'sgd': 131, 'shp': 132, 'sll': 133, 'sos': 134, 'srd': 135, 'std': 136, 'svc': 137, 'syp': 138, 'szl': 139, 'thb': 140, 'tjs': 141, 'tmt': 142, 'tnd': 143, 'top': 144, 'try': 145, 'ttd': 146, 'twd': 147, 'tzs': 148, 'uah': 149, 'ugx': 150, 'usd': 151, 'uyu': 152, 'uzs': 153, 'vef': 154, 'vnd': 155, 'vuv': 156, 'wst': 157, 'xaf': 158, 'xag': 159, 'xau': 160, 'xcd': 161, 'xdr': 162, 'xof': 163, 'xpf': 164, 'xrp': 165, 'yer': 166, 'zar': 167, 'zmk': 168, 'zmw': 169, 'zwl': 170}

      +

      JS-NG> +
      +JS-NG> fake.api_key="VALID KEY" +
      +JS-NG> j.clients.currencylayer.fake.load() +
      +JS-NG> j.clients.currencylayer.fake.id2cur_print() +
      +{1: 'aed', +2: 'afn', +3: 'all', +4: 'amd', +5: 'ang', +6: 'aoa', +7: 'ars', +8: 'aud', +9: 'awg', +10: 'azn', +11: 'bam', +12: 'bbd', +13: 'bdt', +14: 'bgn', +15: 'bhd', +16: 'bif', +17: 'bmd', +18: 'bnd', +19: 'bob', +20: 'brl', +21: 'bsd', +22: 'btc', +23: 'btn', +24: 'bwp', +25: 'byn', +26: 'byr', +27: 'bzd', +28: 'cad', +29: 'cdf', +30: 'chf', +31: 'clf', +32: 'clp', +33: 'cny', +34: 'cop', +35: 'crc', +36: 'cuc', +37: 'cup', +38: 'cve', +39: 'czk', +40: 'djf', +41: 'dkk', +42: 'dop', +43: 'dzd', +44: 'egp', +45: 'ern', +46: 'etb', +47: 'eth', +48: 'eur', +49: 'fjd', +50: 'fkp', +51: 'gbp', +52: 'gel', +53: 'ggp', +54: 'ghs', +55: 'gip', +56: 'gmd', +57: 'gnf', +58: 'gtq', +59: 'gyd', +60: 'hkd', +61: 'hnl', +62: 'hrk', +63: 'htg', +64: 'huf', +65: 'idr', +66: 'ils', +67: 'imp', +68: 'inr', +69: 'iqd', +70: 'irr', +71: 'isk', +72: 'jep', +73: 'jmd', +74: 'jod', +75: 'jpy', +76: 'kes', +77: 'kgs', +78: 'khr', +79: 'kmf', +80: 'kpw', +81: 'krw', +82: 'kwd', +83: 'kyd', +84: 'kzt', +85: 'lak', +86: 'lbp', +87: 'lkr', +88: 'lrd', +89: 'lsl', +90: 'ltl', +91: 'lvl', +92: 'lyd', +93: 'mad', +94: 'mdl', +95: 'mga', +96: 'mkd', +97: 'mmk', +98: 'mnt', +99: 'mop', +100: 'mro', +101: 'mur', +102: 'mvr', +103: 'mwk', +104: 'mxn', +105: 'myr', +106: 'mzn', +107: 'nad', +108: 'ngn', +109: 'nio', +110: 'nok', +111: 'npr', +112: 'nzd', +113: 'omr', +114: 'pab', +115: 'pen', +116: 'pgk', +117: 'php', +118: 'pkr', +119: 'pln', +120: 'pyg', +121: 'qar', +122: 'ron', +123: 'rsd', +124: 'rub', +125: 'rwf', +126: 'sar', +127: 'sbd', +128: 'scr', +129: 'sdg', +130: 'sek', +131: 'sgd', +132: 'shp', +133: 'sll', +134: 'sos', +135: 'srd', +136: 'std', +137: 'svc', +138: 'syp', +139: 'szl', +140: 'thb', +141: 'tjs', +142: 'tmt', +143: 'tnd', +144: 'top', +145: 'try', +146: 'ttd', +147: 'twd', +148: 'tzs', +149: 'uah', +150: 'ugx', +151: 'usd', +152: 'uyu', +153: 'uzs', +154: 'vef', +155: 'vnd', +156: 'vuv', +157: 'wst', +158: 'xaf', +159: 'xag', +160: 'xau', +161: 'xcd', +162: 'xdr', +163: 'xof', +164: 'xpf', +165: 'xrp', +166: 'yer', +167: 'zar', +168: 'zmk', +169: 'zmw', +170: 'zwl'} +JS-NG> j.clients.currencylayer.fake.cur2usd_print() +
      +{'': 1, +'aed': 3.672979, +'afn': 78.296617, +'ah': 24.914996, +'all': 109.150047, +'amd': 476.210221, +'ang': 1.78525, +'aoa': 362.0025, +'ar': 3.75045, +'ars': 55.394992, +'aud': 1.474703, +'awg': 1.8, +'azn': 1.704964, +'bam': 1.758993, +'bbd': 2.0194, +'bd': 8.221403, +'bdt': 83.745499, +'bgn': 1.760801, +'bhd': 0.375961, +'bif': 1855, +'bmd': 1, +'bnd': 1.350696, +'bob': 6.86065, +'brl': 4.152695, +'bsd': 0.99205, +'btc': 9.948852946999476e-05, +'btn': 71.884502, +'bwp': 10.961999, +'byn': 2.060501, +'byr': 19600, +'bzd': 2.01595, +'cad': 1.32733, +'cdf': 1659.99946, +'chf': 0.978545, +'clf': 0.026094, +'clp': 720.00501, +'cny': 7.151304, +'cop': 3431.55, +'cr': 13.669974, +'crc': 567.080062, +'cuc': 1, +'cup': 26.5, +'cve': 98.749501, +'czk': 23.208988, +'egp': 16.53602, +'ek': 9.67235, +'ern': 14.999484, +'etb': 29.000284, +'eth': 0.005395489370885939, +'eur': 0.900035, +'fjd': 2.17495, +'fkp': 0.81691, +'g': 45.119039, +'gbp': 0.81752, +'gd': 1.38792, +'gel': 2.925034, +'ggp': 0.81764, +'ghs': 5.402501, +'gip': 0.81691, +'gmd': 50.415037, +'gnf': 9239.999966, +'gtq': 7.680957, +'gx': 3685.496424, +'gyd': 209.244968, +'hkd': 7.84595, +'hnl': 24.674984, +'hp': 1.320898, +'hrk': 6.653399, +'htg': 95.361503, +'huf': 296.280997, +'idr': 14258.25, +'ils': 3.52095, +'imp': 0.81764, +'inr': 71.792403, +'iqd': 1190, +'irr': 42104.999481, +'isk': 124.829491, +'jep': 0.81764, +'jf': 177.720165, +'jmd': 134.559965, +'jod': 0.7084, +'jpy': 106.015996, +'kes': 103.389937, +'kgs': 69.8159, +'khr': 4140.000279, +'kk': 6.71151, +'kmf': 443.249767, +'kpw': 900.052015, +'krw': 1214.824979, +'kwd': 0.303901, +'kyd': 0.83355, +'kzt': 383.110385, +'lak': 8735.000017, +'lbp': 1507.949729, +'lkr': 179.605474, +'ll': 9299.999946, +'lrd': 205.000232, +'lsl': 15.250149, +'ltl': 2.95274, +'lvl': 0.60489, +'lyd': 1.40503, +'mad': 9.5685, +'mdl': 17.887498, +'mga': 3674.999563, +'mkd': 55.324023, +'mmk': 1516.702673, +'mnt': 2669.391245, +'mop': 8.080496, +'mro': 357.000024, +'mur': 36.043506, +'mvr': 15.410297, +'mwk': 731.210149, +'mxn': 19.92145, +'myr': 4.198897, +'mzn': 61.020166, +'nad': 15.270055, +'ngn': 362.000148, +'nio': 33.602406, +'nok': 8.988065, +'npr': 115.010199, +'nzd': 1.56365, +'omr': 0.384976, +'op': 51.294983, +'os': 579.999893, +'pab': 0.99205, +'pen': 3.37635, +'pgk': 3.397801, +'php': 52.438012, +'pkr': 157.249855, +'pln': 3.92254, +'pyg': 6217.103241, +'qar': 3.64175, +'rd': 7.457963, +'ron': 4.256202, +'rsd': 106.069758, +'rub': 66.06102, +'rwf': 910, +'td': 21560.79, +'thb': 30.589849, +'tjs': 9.696302, +'tmt': 3.5, +'tnd': 2.857701, +'top': 2.320597, +'try': 5.81132, +'ttd': 6.71695, +'twd': 31.400972, +'tzs': 2298.149889, +'vc': 8.75195, +'vef': 9.987501, +'vnd': 23199, +'vuv': 117.90362, +'wst': 2.675215, +'xaf': 589.959986, +'xag': 0.056555, +'xau': 0.000653, +'xcd': 2.70245, +'xdr': 0.729108, +'xof': 584.499865, +'xpf': 106.950279, +'xrp': 3.771, +'yer': 250.349819, +'yp': 515.000236, +'yu': 36.34003, +'zar': 15.26498, +'zd': 119.879946, +'zl': 15.269489, +'zmk': 9001.202171, +'zmw': 13.112024, +'zs': 9376.306597, +'zwl': 322.000001}

      +
      + +Expand source code + +
      """
      +JS-NG> fake = j.clients.currencylayer.new('fake')                                                                      
      +JS-NG> fake.cur2id_print()                                                                                             
      +{'aed': 1,
      + 'afn': 2,
      + 'all': 3,
      + 'amd': 4,
      + 'ang': 5,
      + 'aoa': 6,
      + 'ars': 7,
      + 'aud': 8,
      + 'awg': 9,
      + 'azn': 10,
      + 'bam': 11,
      + 'bbd': 12,
      + 'bdt': 13,
      + 'bgn': 14,
      + 'bhd': 15,
      + 'bif': 16,
      + 'bmd': 17,
      + 'bnd': 18,
      + 'bob': 19,
      + 'brl': 20,
      + 'bsd': 21,
      + 'btc': 22,
      + 'btn': 23,
      + 'bwp': 24,
      + 'byn': 25,
      + 'byr': 26,
      + 'bzd': 27,
      + 'cad': 28,
      + 'cdf': 29,
      + 'chf': 30,
      + 'clf': 31,
      + 'clp': 32,
      + 'cny': 33,
      + 'cop': 34,
      + 'crc': 35,
      + 'cuc': 36,
      + 'cup': 37,
      + 'cve': 38,
      + 'czk': 39,
      + 'djf': 40,
      + 'dkk': 41,
      + 'dop': 42,
      + 'dzd': 43,
      + 'egp': 44,
      + 'ern': 45,
      + 'etb': 46,
      + 'eth': 47,
      + 'eur': 48,
      + 'fjd': 49,
      + 'fkp': 50,
      + 'gbp': 51,
      + 'gel': 52,
      + 'ggp': 53,
      + 'ghs': 54,
      + 'gip': 55,
      + 'gmd': 56,
      + 'gnf': 57,
      + 'gtq': 58,
      + 'gyd': 59,
      + 'hkd': 60,
      + 'hnl': 61,
      + 'hrk': 62,
      + 'htg': 63,
      + 'huf': 64,
      + 'idr': 65,
      + 'ils': 66,
      + 'imp': 67,
      + 'inr': 68,
      + 'iqd': 69,
      + 'irr': 70,
      + 'isk': 71,
      + 'jep': 72,
      + 'jmd': 73,
      + 'jod': 74,
      + 'jpy': 75,
      + 'kes': 76,
      + 'kgs': 77,
      + 'khr': 78,
      + 'kmf': 79,
      + 'kpw': 80,
      + 'krw': 81,
      + 'kwd': 82,
      + 'kyd': 83,
      + 'kzt': 84,
      + 'lak': 85,
      + 'lbp': 86,
      + 'lkr': 87,
      + 'lrd': 88,
      + 'lsl': 89,
      + 'ltl': 90,
      + 'lvl': 91,
      + 'lyd': 92,
      + 'mad': 93,
      + 'mdl': 94,
      + 'mga': 95,
      + 'mkd': 96,
      + 'mmk': 97,
      + 'mnt': 98,
      + 'mop': 99,
      + 'mro': 100,
      + 'mur': 101,
      + 'mvr': 102,
      + 'mwk': 103,
      + 'mxn': 104,
      + 'myr': 105,
      + 'mzn': 106,
      + 'nad': 107,
      + 'ngn': 108,
      + 'nio': 109,
      + 'nok': 110,
      + 'npr': 111,
      + 'nzd': 112,
      + 'omr': 113,
      + 'pab': 114,
      + 'pen': 115,
      + 'pgk': 116,
      + 'php': 117,
      + 'pkr': 118,
      + 'pln': 119,
      + 'pyg': 120,
      + 'qar': 121,
      + 'ron': 122,
      + 'rsd': 123,
      + 'rub': 124,
      + 'rwf': 125,
      + 'sar': 126,
      + 'sbd': 127,
      + 'scr': 128,
      + 'sdg': 129,
      + 'sek': 130,
      + 'sgd': 131,
      + 'shp': 132,
      + 'sll': 133,
      + 'sos': 134,
      + 'srd': 135,
      + 'std': 136,
      + 'svc': 137,
      + 'syp': 138,
      + 'szl': 139,
      + 'thb': 140,
      + 'tjs': 141,
      + 'tmt': 142,
      + 'tnd': 143,
      + 'top': 144,
      + 'try': 145,
      + 'ttd': 146,
      + 'twd': 147,
      + 'tzs': 148,
      + 'uah': 149,
      + 'ugx': 150,
      + 'usd': 151,
      + 'uyu': 152,
      + 'uzs': 153,
      + 'vef': 154,
      + 'vnd': 155,
      + 'vuv': 156,
      + 'wst': 157,
      + 'xaf': 158,
      + 'xag': 159,
      + 'xau': 160,
      + 'xcd': 161,
      + 'xdr': 162,
      + 'xof': 163,
      + 'xpf': 164,
      + 'xrp': 165,
      + 'yer': 166,
      + 'zar': 167,
      + 'zmk': 168,
      + 'zmw': 169,
      + 'zwl': 170}
      +JS-NG> fake.id2cur_print()                                                                                             
      +{1: 'aed',
      + 2: 'afn',
      + 3: 'all',
      + 4: 'amd',
      + 5: 'ang',
      + 6: 'aoa',
      + 7: 'ars',
      + 8: 'aud',
      + 9: 'awg',
      + 10: 'azn',
      + 11: 'bam',
      + 12: 'bbd',
      + 13: 'bdt',
      + 14: 'bgn',
      + 15: 'bhd',
      + 16: 'bif',
      + 17: 'bmd',
      + 18: 'bnd',
      + 19: 'bob',
      + 20: 'brl',
      + 21: 'bsd',
      + 22: 'btc',
      + 23: 'btn',
      + 24: 'bwp',
      + 25: 'byn',
      + 26: 'byr',
      + 27: 'bzd',
      + 28: 'cad',
      + 29: 'cdf',
      + 30: 'chf',
      + 31: 'clf',
      + 32: 'clp',
      + 33: 'cny',
      + 34: 'cop',
      + 35: 'crc',
      + 36: 'cuc',
      + 37: 'cup',
      + 38: 'cve',
      + 39: 'czk',
      + 40: 'djf',
      + 41: 'dkk',
      + 42: 'dop',
      + 43: 'dzd',
      + 44: 'egp',
      + 45: 'ern',
      + 46: 'etb',
      + 47: 'eth',
      + 48: 'eur',
      + 49: 'fjd',
      + 50: 'fkp',
      + 51: 'gbp',
      + 52: 'gel',
      + 53: 'ggp',
      + 54: 'ghs',
      + 55: 'gip',
      + 56: 'gmd',
      + 57: 'gnf',
      + 58: 'gtq',
      + 59: 'gyd',
      + 60: 'hkd',
      + 61: 'hnl',
      + 62: 'hrk',
      + 63: 'htg',
      + 64: 'huf',
      + 65: 'idr',
      + 66: 'ils',
      + 67: 'imp',
      + 68: 'inr',
      + 69: 'iqd',
      + 70: 'irr',
      + 71: 'isk',
      + 72: 'jep',
      + 73: 'jmd',
      + 74: 'jod',
      + 75: 'jpy',
      + 76: 'kes',
      + 77: 'kgs',
      + 78: 'khr',
      + 79: 'kmf',
      + 80: 'kpw',
      + 81: 'krw',
      + 82: 'kwd',
      + 83: 'kyd',
      + 84: 'kzt',
      + 85: 'lak',
      + 86: 'lbp',
      + 87: 'lkr',
      + 88: 'lrd',
      + 89: 'lsl',
      + 90: 'ltl',
      + 91: 'lvl',
      + 92: 'lyd',
      + 93: 'mad',
      + 94: 'mdl',
      + 95: 'mga',
      + 96: 'mkd',
      + 97: 'mmk',
      + 98: 'mnt',
      + 99: 'mop',
      + 100: 'mro',
      + 101: 'mur',
      + 102: 'mvr',
      + 103: 'mwk',
      + 104: 'mxn',
      + 105: 'myr',
      + 106: 'mzn',
      + 107: 'nad',
      + 108: 'ngn',
      + 109: 'nio',
      + 110: 'nok',
      + 111: 'npr',
      + 112: 'nzd',
      + 113: 'omr',
      + 114: 'pab',
      + 115: 'pen',
      + 116: 'pgk',
      + 117: 'php',
      + 118: 'pkr',
      + 119: 'pln',
      + 120: 'pyg',
      + 121: 'qar',
      + 122: 'ron',
      + 123: 'rsd',
      + 124: 'rub',
      + 125: 'rwf',
      + 126: 'sar',
      + 127: 'sbd',
      + 128: 'scr',
      + 129: 'sdg',
      + 130: 'sek',
      + 131: 'sgd',
      + 132: 'shp',
      + 133: 'sll',
      + 134: 'sos',
      + 135: 'srd',
      + 136: 'std',
      + 137: 'svc',
      + 138: 'syp',
      + 139: 'szl',
      + 140: 'thb',
      + 141: 'tjs',
      + 142: 'tmt',
      + 143: 'tnd',
      + 144: 'top',
      + 145: 'try',
      + 146: 'ttd',
      + 147: 'twd',
      + 148: 'tzs',
      + 149: 'uah',
      + 150: 'ugx',
      + 151: 'usd',
      + 152: 'uyu',
      + 153: 'uzs',
      + 154: 'vef',
      + 155: 'vnd',
      + 156: 'vuv',
      + 157: 'wst',
      + 158: 'xaf',
      + 159: 'xag',
      + 160: 'xau',
      + 161: 'xcd',
      + 162: 'xdr',
      + 163: 'xof',
      + 164: 'xpf',
      + 165: 'xrp',
      + 166: 'yer',
      + 167: 'zar',
      + 168: 'zmk',
      + 169: 'zmw',
      + 170: 'zwl'}
      +JS-NG> fake.id2cur                                                                                                     
      +{1: 'aed', 2: 'afn', 3: 'all', 4: 'amd', 5: 'ang', 6: 'aoa', 7: 'ars', 8: 'aud', 9: 'awg', 10: 'azn', 11: 'bam', 12: 'b
      +bd', 13: 'bdt', 14: 'bgn', 15: 'bhd', 16: 'bif', 17: 'bmd', 18: 'bnd', 19: 'bob', 20: 'brl', 21: 'bsd', 22: 'btc', 23: 
      +'btn', 24: 'bwp', 25: 'byn', 26: 'byr', 27: 'bzd', 28: 'cad', 29: 'cdf', 30: 'chf', 31: 'clf', 32: 'clp', 33: 'cny', 34
      +: 'cop', 35: 'crc', 36: 'cuc', 37: 'cup', 38: 'cve', 39: 'czk', 40: 'djf', 41: 'dkk', 42: 'dop', 43: 'dzd', 44: 'egp', 
      +45: 'ern', 46: 'etb', 47: 'eth', 48: 'eur', 49: 'fjd', 50: 'fkp', 51: 'gbp', 52: 'gel', 53: 'ggp', 54: 'ghs', 55: 'gip'
      +, 56: 'gmd', 57: 'gnf', 58: 'gtq', 59: 'gyd', 60: 'hkd', 61: 'hnl', 62: 'hrk', 63: 'htg', 64: 'huf', 65: 'idr', 66: 'ils', 67: 'imp', 68: 'inr', 69: 'iqd', 70: 'irr', 71: 'isk', 72: 'jep', 73: 'jmd', 74: 'jod', 75: 'jpy', 76: 'kes', 77: 'kgs', 78: 'khr', 79: 'kmf', 80: 'kpw', 81: 'krw', 82: 'kwd', 83: 'kyd', 84: 'kzt', 85: 'lak', 86: 'lbp', 87: 'lkr', 88: 'lrd', 89: 'lsl', 90: 'ltl', 91: 'lvl', 92: 'lyd', 93: 'mad', 94: 'mdl', 95: 'mga', 96: 'mkd', 97: 'mmk', 98: 'mnt', 99: 'mop', 100: 'mro', 101: 'mur', 102: 'mvr', 103: 'mwk', 104: 'mxn', 105: 'myr', 106: 'mzn', 107: 'nad', 108: 'ngn', 109: 'nio', 110: 'nok', 111: 'npr', 112: 'nzd', 113: 'omr', 114: 'pab', 115: 'pen', 116: 'pgk', 117: 'php', 118: 'pkr', 119: 'pln', 120: 'pyg', 121: 'qar', 122: 'ron', 123: 'rsd', 124: 'rub', 125: 'rwf', 126: 'sar', 127: 'sbd', 128: 'scr', 129: 'sdg', 130: 'sek', 131: 'sgd', 132: 'shp', 133: 'sll', 134: 'sos', 135: 'srd', 136: 'std', 137: 'svc', 138: 'syp', 139: 'szl', 140: 'thb', 141: 'tjs', 142: 'tmt', 143: 'tnd', 144: 'top', 145: 'try', 146: 'ttd', 147: 'twd', 148: 'tzs', 149: 'uah', 150: 'ugx', 151: 'usd', 152: 'uyu', 153: 'uzs', 154: 'vef', 155: 'vnd', 156: 'vuv', 157: 'wst', 158: 'xaf', 159: 'xag', 160: 'xau', 161: 'xcd', 162: 'xdr', 163: 'xof', 164: 'xpf', 165: 'xrp', 166: 'yer', 167: 'zar', 168: 'zmk', 169: 'zmw', 170: 'zwl'}
      +
      +JS-NG> fake.cur2id                                                                                                     
      +{'aed': 1, 'afn': 2, 'all': 3, 'amd': 4, 'ang': 5, 'aoa': 6, 'ars': 7, 'aud': 8, 'awg': 9, 'azn': 10, 'bam': 11, 'bbd':
      + 12, 'bdt': 13, 'bgn': 14, 'bhd': 15, 'bif': 16, 'bmd': 17, 'bnd': 18, 'bob': 19, 'brl': 20, 'bsd': 21, 'btc': 22, 'btn
      +': 23, 'bwp': 24, 'byn': 25, 'byr': 26, 'bzd': 27, 'cad': 28, 'cdf': 29, 'chf': 30, 'clf': 31, 'clp': 32, 'cny': 33, 'c
      +op': 34, 'crc': 35, 'cuc': 36, 'cup': 37, 'cve': 38, 'czk': 39, 'djf': 40, 'dkk': 41, 'dop': 42, 'dzd': 43, 'egp': 44, 
      +'ern': 45, 'etb': 46, 'eth': 47, 'eur': 48, 'fjd': 49, 'fkp': 50, 'gbp': 51, 'gel': 52, 'ggp': 53, 'ghs': 54, 'gip': 55
      +, 'gmd': 56, 'gnf': 57, 'gtq': 58, 'gyd': 59, 'hkd': 60, 'hnl': 61, 'hrk': 62, 'htg': 63, 'huf': 64, 'idr': 65, 'ils': 66, 'imp': 67, 'inr': 68, 'iqd': 69, 'irr': 70, 'isk': 71, 'jep': 72, 'jmd': 73, 'jod': 74, 'jpy': 75, 'kes': 76, 'kgs': 77, 'khr': 78, 'kmf': 79, 'kpw': 80, 'krw': 81, 'kwd': 82, 'kyd': 83, 'kzt': 84, 'lak': 85, 'lbp': 86, 'lkr': 87, 'lrd': 88, 'lsl': 89, 'ltl': 90, 'lvl': 91, 'lyd': 92, 'mad': 93, 'mdl': 94, 'mga': 95, 'mkd': 96, 'mmk': 97, 'mnt': 98, 'mop': 99, 'mro': 100, 'mur': 101, 'mvr': 102, 'mwk': 103, 'mxn': 104, 'myr': 105, 'mzn': 106, 'nad': 107, 'ngn': 108, 'nio': 109, 'nok': 110, 'npr': 111, 'nzd': 112, 'omr': 113, 'pab': 114, 'pen': 115, 'pgk': 116, 'php': 117, 'pkr': 118, 'pln': 119, 'pyg': 120, 'qar': 121, 'ron': 122, 'rsd': 123, 'rub': 124, 'rwf': 125, 'sar': 126, 'sbd': 127, 'scr': 128, 'sdg': 129, 'sek': 130, 'sgd': 131, 'shp': 132, 'sll': 133, 'sos': 134, 'srd': 135, 'std': 136, 'svc': 137, 'syp': 138, 'szl': 139, 'thb': 140, 'tjs': 141, 'tmt': 142, 'tnd': 143, 'top': 144, 'try': 145, 'ttd': 146, 'twd': 147, 'tzs': 148, 'uah': 149, 'ugx': 150, 'usd': 151, 'uyu': 152, 'uzs': 153, 'vef': 154, 'vnd': 155, 'vuv': 156, 'wst': 157, 'xaf': 158, 'xag': 159, 'xau': 160, 'xcd': 161, 'xdr': 162, 'xof': 163, 'xpf': 164, 'xrp': 165, 'yer': 166, 'zar': 167, 'zmk': 168, 'zmw': 169, 'zwl': 170}
      +
      +JS-NG>                                                                                                                 
      +JS-NG> fake.api_key="VALID KEY"                                                                 
      +JS-NG> j.clients.currencylayer.fake.load()                                                                             
      +JS-NG> j.clients.currencylayer.fake.id2cur_print()                                                                     
      +{1: 'aed',
      + 2: 'afn',
      + 3: 'all',
      + 4: 'amd',
      + 5: 'ang',
      + 6: 'aoa',
      + 7: 'ars',
      + 8: 'aud',
      + 9: 'awg',
      + 10: 'azn',
      + 11: 'bam',
      + 12: 'bbd',
      + 13: 'bdt',
      + 14: 'bgn',
      + 15: 'bhd',
      + 16: 'bif',
      + 17: 'bmd',
      + 18: 'bnd',
      + 19: 'bob',
      + 20: 'brl',
      + 21: 'bsd',
      + 22: 'btc',
      + 23: 'btn',
      + 24: 'bwp',
      + 25: 'byn',
      + 26: 'byr',
      + 27: 'bzd',
      + 28: 'cad',
      + 29: 'cdf',
      + 30: 'chf',
      + 31: 'clf',
      + 32: 'clp',
      + 33: 'cny',
      + 34: 'cop',
      + 35: 'crc',
      + 36: 'cuc',
      + 37: 'cup',
      + 38: 'cve',
      + 39: 'czk',
      + 40: 'djf',
      + 41: 'dkk',
      + 42: 'dop',
      + 43: 'dzd',
      + 44: 'egp',
      + 45: 'ern',
      + 46: 'etb',
      + 47: 'eth',
      + 48: 'eur',
      + 49: 'fjd',
      + 50: 'fkp',
      + 51: 'gbp',
      + 52: 'gel',
      + 53: 'ggp',
      + 54: 'ghs',
      + 55: 'gip',
      + 56: 'gmd',
      + 57: 'gnf',
      + 58: 'gtq',
      + 59: 'gyd',
      + 60: 'hkd',
      + 61: 'hnl',
      + 62: 'hrk',
      + 63: 'htg',
      + 64: 'huf',
      + 65: 'idr',
      + 66: 'ils',
      + 67: 'imp',
      + 68: 'inr',
      + 69: 'iqd',
      + 70: 'irr',
      + 71: 'isk',
      + 72: 'jep',
      + 73: 'jmd',
      + 74: 'jod',
      + 75: 'jpy',
      + 76: 'kes',
      + 77: 'kgs',
      + 78: 'khr',
      + 79: 'kmf',
      + 80: 'kpw',
      + 81: 'krw',
      + 82: 'kwd',
      + 83: 'kyd',
      + 84: 'kzt',
      + 85: 'lak',
      + 86: 'lbp',
      + 87: 'lkr',
      + 88: 'lrd',
      + 89: 'lsl',
      + 90: 'ltl',
      + 91: 'lvl',
      + 92: 'lyd',
      + 93: 'mad',
      + 94: 'mdl',
      + 95: 'mga',
      + 96: 'mkd',
      + 97: 'mmk',
      + 98: 'mnt',
      + 99: 'mop',
      + 100: 'mro',
      + 101: 'mur',
      + 102: 'mvr',
      + 103: 'mwk',
      + 104: 'mxn',
      + 105: 'myr',
      + 106: 'mzn',
      + 107: 'nad',
      + 108: 'ngn',
      + 109: 'nio',
      + 110: 'nok',
      + 111: 'npr',
      + 112: 'nzd',
      + 113: 'omr',
      + 114: 'pab',
      + 115: 'pen',
      + 116: 'pgk',
      + 117: 'php',
      + 118: 'pkr',
      + 119: 'pln',
      + 120: 'pyg',
      + 121: 'qar',
      + 122: 'ron',
      + 123: 'rsd',
      + 124: 'rub',
      + 125: 'rwf',
      + 126: 'sar',
      + 127: 'sbd',
      + 128: 'scr',
      + 129: 'sdg',
      + 130: 'sek',
      + 131: 'sgd',
      + 132: 'shp',
      + 133: 'sll',
      + 134: 'sos',
      + 135: 'srd',
      + 136: 'std',
      + 137: 'svc',
      + 138: 'syp',
      + 139: 'szl',
      + 140: 'thb',
      + 141: 'tjs',
      + 142: 'tmt',
      + 143: 'tnd',
      + 144: 'top',
      + 145: 'try',
      + 146: 'ttd',
      + 147: 'twd',
      + 148: 'tzs',
      + 149: 'uah',
      + 150: 'ugx',
      + 151: 'usd',
      + 152: 'uyu',
      + 153: 'uzs',
      + 154: 'vef',
      + 155: 'vnd',
      + 156: 'vuv',
      + 157: 'wst',
      + 158: 'xaf',
      + 159: 'xag',
      + 160: 'xau',
      + 161: 'xcd',
      + 162: 'xdr',
      + 163: 'xof',
      + 164: 'xpf',
      + 165: 'xrp',
      + 166: 'yer',
      + 167: 'zar',
      + 168: 'zmk',
      + 169: 'zmw',
      + 170: 'zwl'}
      +JS-NG> j.clients.currencylayer.fake.cur2usd_print()                                                                    
      +{'': 1,
      + 'aed': 3.672979,
      + 'afn': 78.296617,
      + 'ah': 24.914996,
      + 'all': 109.150047,
      + 'amd': 476.210221,
      + 'ang': 1.78525,
      + 'aoa': 362.0025,
      + 'ar': 3.75045,
      + 'ars': 55.394992,
      + 'aud': 1.474703,
      + 'awg': 1.8,
      + 'azn': 1.704964,
      + 'bam': 1.758993,
      + 'bbd': 2.0194,
      + 'bd': 8.221403,
      + 'bdt': 83.745499,
      + 'bgn': 1.760801,
      + 'bhd': 0.375961,
      + 'bif': 1855,
      + 'bmd': 1,
      + 'bnd': 1.350696,
      + 'bob': 6.86065,
      + 'brl': 4.152695,
      + 'bsd': 0.99205,
      + 'btc': 9.948852946999476e-05,
      + 'btn': 71.884502,
      + 'bwp': 10.961999,
      + 'byn': 2.060501,
      + 'byr': 19600,
      + 'bzd': 2.01595,
      + 'cad': 1.32733,
      + 'cdf': 1659.99946,
      + 'chf': 0.978545,
      + 'clf': 0.026094,
      + 'clp': 720.00501,
      + 'cny': 7.151304,
      + 'cop': 3431.55,
      + 'cr': 13.669974,
      + 'crc': 567.080062,
      + 'cuc': 1,
      + 'cup': 26.5,
      + 'cve': 98.749501,
      + 'czk': 23.208988,
      + 'egp': 16.53602,
      + 'ek': 9.67235,
      + 'ern': 14.999484,
      + 'etb': 29.000284,
      + 'eth': 0.005395489370885939,
      + 'eur': 0.900035,
      + 'fjd': 2.17495,
      + 'fkp': 0.81691,
      + 'g': 45.119039,
      + 'gbp': 0.81752,
      + 'gd': 1.38792,
      + 'gel': 2.925034,
      + 'ggp': 0.81764,
      + 'ghs': 5.402501,
      + 'gip': 0.81691,
      + 'gmd': 50.415037,
      + 'gnf': 9239.999966,
      + 'gtq': 7.680957,
      + 'gx': 3685.496424,
      + 'gyd': 209.244968,
      + 'hkd': 7.84595,
      + 'hnl': 24.674984,
      + 'hp': 1.320898,
      + 'hrk': 6.653399,
      + 'htg': 95.361503,
      + 'huf': 296.280997,
      + 'idr': 14258.25,
      + 'ils': 3.52095,
      + 'imp': 0.81764,
      + 'inr': 71.792403,
      + 'iqd': 1190,
      + 'irr': 42104.999481,
      + 'isk': 124.829491,
      + 'jep': 0.81764,
      + 'jf': 177.720165,
      + 'jmd': 134.559965,
      + 'jod': 0.7084,
      + 'jpy': 106.015996,
      + 'kes': 103.389937,
      + 'kgs': 69.8159,
      + 'khr': 4140.000279,
      + 'kk': 6.71151,
      + 'kmf': 443.249767,
      + 'kpw': 900.052015,
      + 'krw': 1214.824979,
      + 'kwd': 0.303901,
      + 'kyd': 0.83355,
      + 'kzt': 383.110385,
      + 'lak': 8735.000017,
      + 'lbp': 1507.949729,
      + 'lkr': 179.605474,
      + 'll': 9299.999946,
      + 'lrd': 205.000232,
      + 'lsl': 15.250149,
      + 'ltl': 2.95274,
      + 'lvl': 0.60489,
      + 'lyd': 1.40503,
      + 'mad': 9.5685,
      + 'mdl': 17.887498,
      + 'mga': 3674.999563,
      + 'mkd': 55.324023,
      + 'mmk': 1516.702673,
      + 'mnt': 2669.391245,
      + 'mop': 8.080496,
      + 'mro': 357.000024,
      + 'mur': 36.043506,
      + 'mvr': 15.410297,
      + 'mwk': 731.210149,
      + 'mxn': 19.92145,
      + 'myr': 4.198897,
      + 'mzn': 61.020166,
      + 'nad': 15.270055,
      + 'ngn': 362.000148,
      + 'nio': 33.602406,
      + 'nok': 8.988065,
      + 'npr': 115.010199,
      + 'nzd': 1.56365,
      + 'omr': 0.384976,
      + 'op': 51.294983,
      + 'os': 579.999893,
      + 'pab': 0.99205,
      + 'pen': 3.37635,
      + 'pgk': 3.397801,
      + 'php': 52.438012,
      + 'pkr': 157.249855,
      + 'pln': 3.92254,
      + 'pyg': 6217.103241,
      + 'qar': 3.64175,
      + 'rd': 7.457963,
      + 'ron': 4.256202,
      + 'rsd': 106.069758,
      + 'rub': 66.06102,
      + 'rwf': 910,
      + 'td': 21560.79,
      + 'thb': 30.589849,
      + 'tjs': 9.696302,
      + 'tmt': 3.5,
      + 'tnd': 2.857701,
      + 'top': 2.320597,
      + 'try': 5.81132,
      + 'ttd': 6.71695,
      + 'twd': 31.400972,
      + 'tzs': 2298.149889,
      + 'vc': 8.75195,
      + 'vef': 9.987501,
      + 'vnd': 23199,
      + 'vuv': 117.90362,
      + 'wst': 2.675215,
      + 'xaf': 589.959986,
      + 'xag': 0.056555,
      + 'xau': 0.000653,
      + 'xcd': 2.70245,
      + 'xdr': 0.729108,
      + 'xof': 584.499865,
      + 'xpf': 106.950279,
      + 'xrp': 3.771,
      + 'yer': 250.349819,
      + 'yp': 515.000236,
      + 'yu': 36.34003,
      + 'zar': 15.26498,
      + 'zd': 119.879946,
      + 'zl': 15.269489,
      + 'zmk': 9001.202171,
      + 'zmw': 13.112024,
      + 'zs': 9376.306597,
      + 'zwl': 322.000001}
      +
      +
      +"""
      +
      +
      +def export_module_as():
      +    from jumpscale.core.base import StoredFactory
      +    from .currencylayer import CurrencyLayerClient
      +    return StoredFactory(CurrencyLayerClient)
      +
      +
      +
      +

      Sub-modules

      +
      +
      jumpscale.clients.currencylayer.currencies
      +
      +
      +
      +
      jumpscale.clients.currencylayer.currencylayer
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import StoredFactory
      +    from .currencylayer import CurrencyLayerClient
      +    return StoredFactory(CurrencyLayerClient)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/digitalocean/digitalocean.html b/docs/api/jumpscale/clients/digitalocean/digitalocean.html new file mode 100644 index 000000000..c62b7b285 --- /dev/null +++ b/docs/api/jumpscale/clients/digitalocean/digitalocean.html @@ -0,0 +1,2911 @@ + + + + + + +jumpscale.clients.digitalocean.digitalocean API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.digitalocean.digitalocean

      +
      +
      +

      This module is used to manage your digital ocean account, create droplet,list all the droplets, destroy droplets, create project, list all the projects and delete project

      +

      prerequisites

      +

      Sshkey client and loaded with you public key

      +

      '''python +ssh = j.clients.sshkey.get(name= "test") +ssh.private_key_path = "/home/rafy/.ssh/id_rsa" +ssh.load_from_file_system() +'''

      +

      Create Digital Ocean client and set your token and load your sshkey

      +
      dg = j.clients.digitalocean.get("testDG")
      +dg.token = ""
      +
      +

      Set sshclient you have created

      +
      dg.set_default_sshkey(ssh)
      +
      +

      Use Digital Ocean client

      +

      Create and deploy Project to Digital Ocean

      +

      Create Project (name must not contian spaces or start with number)

      +
       project = dg.projects.new("test_DG_client")
      +
      +

      Set the name you want to deploy with on Digital Ocean

      +
      project.set_digital_ocean_name("test project DO client")
      +
      +

      Deploy the project (you have to specific the purpose)

      +
      project.deploy(purpose="testing digital ocean client")
      +
      +

      Deploy the project as default one so the new droplets will be automatically added to your default project

      +
      project.deploy(purpose="testing digital ocean client",is_default=True) 
      +
      +

      Delete the project from Digital Ocean

      +
      project.delete_remote()
      +
      +

      Create and deploy Droplet to Digital Ocean

      +

      Create Droplet (name must not contian spaces or start with number)

      +
      droplet = dg.droplets.new("test_droplet_dg")
      +
      +

      Set the name you want to deploy with on Digital Ocean

      +
      droplet.set_digital_ocean_name("droplet-test-DO")
      +
      +

      Deploy the Droplet

      +

      The droplet will be deployed to you your default project

      +
      droplet.deploy()
      +
      +

      You can specify the project that you want the deploy the droplet to

      +
      droplet.deploy(project_name="test project DO client")
      +
      +

      Delete the Droplet from Digital Ocean

      +
      droplet.delete_remote()
      +
      +

      Get digital ocean regions

      +
      dg.regions
      +
      +

      Get digital ocean images

      +
      dg.images
      +
      +

      In the below examples, I have supposed that you followed the above steps +and you got digital ocean client with the name (dg)

      +
      + +Expand source code + +
      """This module is used to manage your digital ocean account, create droplet,list all the droplets, destroy droplets, create project, list all the projects and delete project
      +# prerequisites
      +## Sshkey client and loaded with you public key
      +'''python
      +ssh = j.clients.sshkey.get(name= "test")
      +ssh.private_key_path = "/home/rafy/.ssh/id_rsa"
      +ssh.load_from_file_system()
      +'''
      +## Create Digital Ocean client and set your token and load your sshkey
      +```python
      +dg = j.clients.digitalocean.get("testDG")
      +dg.token = ""
      +```
      +## Set sshclient you have created
      +``` python
      +dg.set_default_sshkey(ssh)
      +```
      +## Use Digital Ocean client
      +
      +### Create and deploy Project to Digital Ocean
      +
      +#### Create Project (name must not contian spaces or start with number)
      +``` python
      + project = dg.projects.new("test_DG_client")
      +```
      +#### Set the name you want to deploy with on Digital Ocean
      +``` python
      +project.set_digital_ocean_name("test project DO client")
      +```
      +#### Deploy the project (you have to specific the purpose)
      +``` python
      +project.deploy(purpose="testing digital ocean client")
      +```
      +#### Deploy the project as default one so the new droplets will be automatically added to your default project
      +``` python
      +project.deploy(purpose="testing digital ocean client",is_default=True) 
      +```
      +#### Delete the project from Digital Ocean
      +``` python
      +project.delete_remote()
      +```
      +
      +### Create and deploy Droplet to Digital Ocean
      +
      +#### Create Droplet (name must not contian spaces or start with number)
      +``` python
      +droplet = dg.droplets.new("test_droplet_dg")
      +```
      +#### Set the name you want to deploy with on Digital Ocean
      +``` python
      +droplet.set_digital_ocean_name("droplet-test-DO")
      +```
      +#### Deploy the Droplet
      +The droplet will be deployed to you your default project
      +```python
      +droplet.deploy()
      +```
      +You can specify the project that you want the deploy the droplet to 
      +```python
      +droplet.deploy(project_name="test project DO client")
      +```
      +#### Delete the Droplet from Digital Ocean
      +```python
      +droplet.delete_remote()
      +```
      +
      +### Get digital ocean regions
      +```python
      +dg.regions
      +```
      +
      +### Get digital ocean images
      +```python
      +dg.images
      +```
      +
      +In the below examples, I have supposed that you followed the above steps
      +and you got digital ocean client with the name (dg)
      +"""
      +
      +from jumpscale.loader import j
      +from jumpscale.clients.base import Client
      +from jumpscale.core.base import fields
      +from jumpscale.core.base import StoredFactory
      +from .project import ProjectManagement
      +import digitalocean
      +
      +
      +class ProjectFactory(StoredFactory):
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +
      +    def list_remote(self):
      +        """
      +        Returns list of projects on Digital Ocean
      +
      +        e.g
      +            dg.projects.list_remote()  -> list of projects
      +
      +        Returns
      +            list(projects): list of projects on digital ocean
      +
      +        """
      +        return ProjectManagement.list(self.parent_instance.client)
      +
      +    def check_project_exist_remote(self, name):
      +        """
      +        Check a project with specific name exits on Digital Ocean
      +
      +        e.g
      +            dg.projects.check_project_exist_remote("codescalers")  -> True
      +            dg.projects.check_project_exist_remote("dsrfjsdfjl")  -> False
      +
      +        Args
      +            name (str): name of the project
      +
      +        Returns
      +            bool : True if the project exits and False if the project does not exist on digital ocean
      +        """
      +        for project in self.list_remote():
      +            if project.name == name:
      +                return True
      +        return False
      +
      +    def get_project_exist_remote(self, name):
      +        """
      +        Get a project with specifc name from  Digital Ocean.
      +
      +        e.g
      +            dg.projects.get_project_exist_remote("codescalers")  -> project
      +
      +        Args
      +            name (str): name of the project
      +
      +        Returns
      +            Project : a project from digital ocean with the name specified
      +        """
      +        for project in self.list_remote():
      +            if project.name == name:
      +                return project
      +        raise j.exceptions.Input("could not find project with name:%s on your Digital Ocean account" % name)
      +
      +
      +class Project(Client):
      +    do_name = fields.String()
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +
      +    def set_digital_ocean_name(self, name):
      +        """Set a name for your project to be used on Digital Ocean
      +        e.g
      +            project.set_digital_ocean_name("test project DO client")
      +
      +        Args:
      +            name (str): name to be used on digital ocean
      +        """
      +        self.do_name = name
      +
      +    def get_digital_ocean_name(self):
      +        """Get a name for the project which is used on digital ocean
      +        e.g
      +            project.get_digital_ocean_name()  ->  "test project DO client"
      +
      +        Returns:
      +            str: name for the project which is used on digital ocean
      +        """
      +        return self.do_name
      +
      +    def deploy(self, purpose, description="", environment="", is_default=False):
      +        """Create a digital ocean project
      +        e.g
      +            project.deploy(purpose="testing digital ocean client")  -> project
      +        Args:
      +            purpose(str): purpose of the project (not optional)
      +            description(str): description of the project, defaults to ""
      +            environment(str): environment of project's resources, defaults to ""
      +            is_default(bool): make this the default project for your user
      +
      +        Returns:
      +            project: The project object that has been created
      +        """
      +
      +        if self.parent.projects.check_project_exist_remote(self.do_name):
      +            raise j.exceptions.Value("A project with the same name already exists")
      +
      +        project = ProjectManagement(
      +            token=self.parent.projects.parent_instance.token,
      +            name=self.do_name,
      +            purpose=purpose,
      +            description=description,
      +            environment=environment,
      +            is_default=is_default,
      +        )
      +        project.create()
      +
      +        if is_default:
      +            project.update(is_default=True)
      +
      +        return project
      +
      +    def delete_remote(self):
      +        """Delete the project from Digital Ocean (A project can't be deleted unless it has no resources.)
      +
      +        e.g
      +            project.delete_remote()
      +        """
      +        project = self.parent.projects.get_project_exist_remote(self.do_name)
      +        project.delete()
      +
      +
      +class DropletFactory(StoredFactory):
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +
      +    def list_remote(self, project_name=None):
      +        """
      +        List all remote droplet or list droplets for a project if it is specified
      +
      +        e.g
      +            dg.droplets.list_remote()  -> list of droplets
      +            dg.droplets.list_remote("codescalers")  -> list of droplets on codescalers project
      +
      +        Args:
      +            project_name (str) : name of project on digital ocean (optional)
      +
      +        Returns
      +            list (Droplet) : list of droplets on digital ocean
      +
      +
      +        """
      +        if project_name:
      +            project = self.parent_instance.projects.get_project_exist_remote(project_name)
      +            return project.list_droplets()
      +
      +        return self.parent_instance.client.get_all_droplets()
      +
      +    def check_droplet_exist_remote(self, name):
      +        """
      +        Check droplet exists on digital ocean
      +
      +        e.g
      +            dg.droplets.check_droplet_exist_remote("3git")  -> True
      +            dg.droplets.check_droplet_exist_remote("sdfgdfed")  -> False
      +
      +        Args:
      +            name (str) : name of droplet
      +
      +        Returns
      +            bool : True if the droplet exist or False if the droplet does not exist
      +        """
      +        for droplet in self.list_remote():
      +            if droplet.name.lower() == name.lower():
      +                return True
      +        return False
      +
      +    def get_droplet_exist_remote(self, name):
      +        """
      +        Get Droplet exists from Digital Ocean
      +
      +        e.g
      +            dg.droplets.get_droplet_exist_remote("3git")
      +
      +        Args:
      +            name (str) : name of droplet
      +
      +        Returns
      +            droplet : droplet with the name specified
      +
      +        """
      +        for droplet in self.list_remote():
      +            if droplet.name.lower() == name.lower():
      +                return droplet
      +        raise j.exceptions.Input("could not find droplet with name:%s on your Digital Ocean account" % name)
      +
      +    def shutdown_all(self, project_name=None):
      +        """
      +        Shutdown all the droplets or droplets in specific project
      +
      +        e.g
      +            dg.droplets.shutdown_all("codescalers")
      +            dg.droplets.shutdown_all()
      +
      +        Args:
      +            name (str) : name of the project
      +
      +        """
      +        for droplet in self.list_remote(project_name):
      +            droplet.shutdown()
      +
      +    def delete_all(self, ignore=None, interactive=True, project_name=None):
      +        """
      +        Delete all the droplets or delete all the droplets in specific project
      +
      +        e.g
      +            dg.droplets.delete_all(project_name = "codescalers")
      +            dg.droplets.delete_all()
      +
      +        Args:
      +            project_name (str) : name of the project
      +            ignore (list): list of ignored droplets to prevent their deletion
      +            interactive (bool): if True the deletion will be interactive and
      +                                confirm if you want to delete but if False it
      +                                will delete directly
      +        """
      +        if not ignore:
      +            ignore = []
      +
      +        def test(ignore, name):
      +            if name.startswith("TF-"):
      +                return False
      +            for item in ignore:
      +                if name.lower().find(item.lower()) != -1:
      +                    return False
      +            return True
      +
      +        todo = []
      +        for droplet in self.list_remote(project_name):
      +            if test(ignore, droplet.name):
      +                todo.append(droplet)
      +        if todo != []:
      +            todotxt = ",".join([i.name for i in todo])
      +            if not interactive or j.tools.console.ask_yes_no("ok to delete:%s" % todotxt):
      +                for droplet in todo:
      +                    droplet.destroy()
      +
      +
      +class Droplet(Client):
      +    do_name = fields.String()
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +
      +    def set_digital_ocean_name(self, name):
      +        """Set a name for your Droplet to be used on Digital Ocean
      +
      +        e.g
      +            droplet.set_digital_ocean_name("test-name")
      +
      +        Args:
      +            name (str): name to be used on digital ocean
      +        """
      +        self.do_name = name
      +
      +    def get_digital_ocean_name(self):
      +        """Get a name for the Droplet which is used on digital ocean
      +
      +        e.g
      +            droplet.get_digital_ocean_name()  ->  "test-name"
      +
      +        Returns
      +            str: name for the droplet which is used on digital ocean
      +        """
      +        return self.do_name
      +
      +    def deploy(
      +        self,
      +        sshkey=None,
      +        region="Amsterdam 3",
      +        image="ubuntu 18.04",
      +        size_slug="s-1vcpu-2gb",
      +        delete=True,
      +        project_name=None,
      +    ):
      +        """
      +        Deploy your Droplet to Digital Ocean
      +
      +        Args
      +            sshkey (string): sshkey name used on digital ocean (if not set it will use the default one which already loaded)
      +            region (string): region name to deploy to
      +            image (string): Image name to be used
      +            size_slug (string): size of the droplet  (s-1vcpu-2gb,s-6vcpu-16gb,gd-8vcpu-32gb)
      +            delete (bool): delete the droplet if it is already deployed on digital ocean
      +            project_name (string): project to add this droplet it. If not specified the default project will be used.
      +
      +        """
      +        project = None
      +        if project_name:
      +            project = self.parent.projects.get_project_exist_remote(project_name)
      +            if not project:
      +                raise j.exceptions.Input("could not find project with name:%s" % project_name)
      +
      +        # Get ssh
      +        if not sshkey:
      +            sshkey_do = self.parent.get_default_sshkey()
      +            if not sshkey_do:
      +                # means we did not find the sshkey on digital ocean yet, need to create
      +                sshkey = self.parent.sshkey
      +
      +                key = digitalocean.SSHKey(
      +                    token=self.parent.projects.parent_instance.token, name=sshkey.name, public_key=sshkey.public_key
      +                )
      +                key.create()
      +                sshkey_do = self.parent.get_default_sshkey()
      +            assert sshkey_do
      +            sshkey = sshkey_do.name
      +
      +        if self.parent.droplets.check_droplet_exist_remote(self.do_name):
      +            dr0 = self.parent.droplets.get_droplet_exist_remote(self.do_name)
      +            if delete:
      +                dr0.destroy()
      +            else:
      +                sshcl = j.clients.sshclient.get(name="do_%s" % self.do_name, host=dr0.ip_address, sshkey=sshkey)
      +                return dr0, sshcl
      +
      +        sshkey = self.parent.droplets.parent_instance.get_sshkey(sshkey)
      +        region = self.parent.droplets.parent_instance.get_region(region)
      +        imagedo = self.parent.droplets.parent_instance.get_image(image)
      +
      +        img_slug_or_id = imagedo.slug if imagedo.slug else imagedo.id
      +
      +        droplet = digitalocean.Droplet(
      +            token=self.parent.droplets.parent_instance.token,
      +            name=self.do_name,
      +            region=region.slug,
      +            image=img_slug_or_id,
      +            size_slug=size_slug,
      +            ssh_keys=[sshkey],
      +            backups=False,
      +        )
      +        droplet.create()
      +
      +        if project:
      +            project.assign_resources(["do:droplet:%s" % droplet.id])
      +
      +    def delete_remote(self):
      +        """Delete Droplet from digital ocean
      +
      +        e.g
      +            droplet.delete_remote()
      +
      +        """
      +        droplet = self.parent.droplets.get_droplet_exist_remote(self.do_name)
      +        droplet.destroy()
      +
      +
      +class DigitalOcean(Client):
      +    name = fields.String()
      +    token = fields.Secret()
      +    projects = fields.Factory(Project, factory_type=ProjectFactory)
      +    droplets = fields.Factory(Droplet, factory_type=DropletFactory)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._client = None
      +
      +    @property
      +    def client(self):
      +        """Return a new client if it is not set or it will return the already existed one
      +
      +        e.g
      +            dg.client  -> <Manager>
      +        Returns
      +            Manager: client form digital ocean manager
      +        """
      +
      +        if not self._client:
      +            self._client = digitalocean.Manager(token=self.token)
      +        return self._client
      +
      +    # Images
      +    @property
      +    def images(self):
      +        """Return a list of digital ocean availabe images
      +
      +        e.g
      +            dg.images  -> [<Image: 31354013 CentOS 6.9 x32>,
      +                                         <Image: 34902021 CentOS 6.9 x64>,...]
      +
      +        Returns
      +            List : list of images on digital ocean available
      +        """
      +        return self.client.get_distro_images()
      +
      +    @property
      +    def myimages(self):
      +        """Return a list of digital ocean images, you have created
      +
      +        e.g
      +            dg.myimages  -> [<Image: 48614453 Unknown Zero_OS>,
      +                                        <Image: 50898718 Ubuntu JumpScale>,...]
      +
      +        Returns
      +            List : list of images on digital ocean, you have created
      +        """
      +        return self.client.get_images(private=True)
      +
      +    @property
      +    def account_images(self):
      +        """Return a list of digital ocean images and the images you have created
      +
      +        e.g
      +            dg.account_images  -> [<Image: 31354013 CentOS 6.9 x32>,
      +                                             <Image: 34902021 CentOS 6.9 x64>,...]
      +
      +        Returns
      +            List : list of images on digital ocean images and the images you have created
      +        """
      +
      +        return self.images + self.myimages
      +
      +    def get_image(self, name):
      +        """Return an image
      +
      +        e.g
      +            dg.get_image(name="CentOS")  -> <Image: 31354013 CentOS 6.9 x32>
      +
      +        Args
      +            name (str): name of the  required image
      +        Returns
      +            Image : list of images on digital ocean images and the images you have created
      +        """
      +        for item in self.account_images:
      +            if item.description:
      +                name_do1 = item.description.lower()
      +            else:
      +                name_do1 = ""
      +            name_do2 = item.distribution + " " + item.name
      +            print(f" - {name_do1}--{name_do2}")
      +            if name_do1.lower().find(name.lower()) != -1 or name_do2.lower().find(name.lower()) != -1:
      +                return item
      +        raise j.exceptions.Base("did not find image:%s" % name)
      +
      +    def get_image_names(self, name=""):
      +        """ Return all the image  or images with a specified name
      +         e.g
      +            dg.get_image_names()  -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602',...]
      +            dg.get_image_names("centos") -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602']
      +
      +        Args
      +            name (str): name of the  required image
      +        Returns
      +            Image : list of images
      +        """
      +        res = []
      +        name = name.lower()
      +        for item in self.images:
      +            if item.description:
      +                name_do = item.description.lower()
      +            else:
      +                name_do = item.distribution + " " + item.name
      +            if name_do.find(name) != -1:
      +                res.append(name_do)
      +        return res
      +
      +    # Size
      +
      +    @property
      +    def sizes(self):
      +        """Return a list sizes available on digital ocean
      +
      +        e.g
      +            dg.sizes -> [s-1vcpu-1gb, 512mb, s-1vcpu-2gb, 1gb, s-3vcpu-1gb,.....]
      +
      +        Returns
      +            List : list of sizes
      +        """
      +        return self.client.get_all_sizes()
      +
      +    # Regions
      +
      +    @property
      +    def regions(self):
      +        """Return a list regions available on digital ocean
      +
      +        e.g
      +            dg.regions  -> [<Region: nyc1 New York 1>, <Region: sgp1 Singapore 1>,...]
      +
      +        Returns
      +            List : list of regions
      +        """
      +        return self.client.get_all_regions()
      +
      +    @property
      +    def region_names(self):
      +        """Returns Digital Ocean regions
      +
      +        e.g
      +            dg.region_names  -> ['nyc1', 'sgp1', 'lon1', 'nyc3', 'ams3', 'fra1', 'tor1', 'sfo2', 'blr1']
      +
      +        Returns
      +            list : list of digital ocean regions
      +        """
      +        return [i.slug for i in self.regions]
      +
      +    def get_region(self, name):
      +        """
      +        Returns specific region
      +
      +        e.g
      +            dg.get_region(name = 'nyc1')  -> <Region: nyc1 New York 1>
      +
      +        Args
      +            name (str) : name of the required region
      +        Returns
      +            Region : the region with the name specified
      +        """
      +        for item in self.regions:
      +            if name == item.slug:
      +                return item
      +            if name == item.name:
      +                return item
      +        raise j.exceptions.Base("did not find region:%s" % name)
      +
      +    # SSHkeys
      +    @property
      +    def sshkeys(self):
      +        """
      +        Return list of sshkeys on digital ocean
      +
      +        e.g
      +            dg.sshkeys  -> [<SSHKey: 25882170 3bot_container_sandbox>,
      +                             <SSHKey: 27130645 Geert-root>,...]
      +
      +        Returns
      +            list : list of sshkeys
      +        """
      +
      +        return self.client.get_all_sshkeys()
      +
      +    def get_default_sshkey(self):
      +        """
      +        Return sshkey you have added to your Digital Ocean client
      +
      +        e.g
      +            dg.get_default_sshkey()  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +        Returns
      +            list : list of sshkeys
      +        """
      +        pubkeyonly = self.sshkey.public_key
      +        for item in self.sshkeys:
      +            if item.public_key.find(pubkeyonly) != -1:
      +                return item
      +        return None
      +
      +    def set_default_sshkey(self, default_sshkey):
      +        """
      +        Set sshkey you  Digital Ocean client
      +
      +        e.g
      +            dg.set_default_sshkey(ssh)  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +        Args
      +            default_sshkey (SSHKeyClient) : sshkey client you have created
      +        """
      +        self.sshkey = default_sshkey
      +
      +    def get_sshkey(self, name):
      +        """
      +        get sshkey from  Digital Ocean
      +
      +        e.g
      +            dg.get_sshkey("rafy@rafy-Inspiron-3576")   ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +        Args
      +            name (string) : sshkey name
      +
      +        Returns
      +            SSHKey : return the specified sshkey
      +        """
      +        for item in self.sshkeys:
      +            if name == item.name:
      +                return item
      +        raise j.exceptions.Base("did not find key:%s" % name)
      +
      +    def __str__(self):
      +        return "digital ocean client:%s" % self.name
      +
      +    __repr__ = __str__
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class DigitalOcean +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class DigitalOcean(Client):
      +    name = fields.String()
      +    token = fields.Secret()
      +    projects = fields.Factory(Project, factory_type=ProjectFactory)
      +    droplets = fields.Factory(Droplet, factory_type=DropletFactory)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._client = None
      +
      +    @property
      +    def client(self):
      +        """Return a new client if it is not set or it will return the already existed one
      +
      +        e.g
      +            dg.client  -> <Manager>
      +        Returns
      +            Manager: client form digital ocean manager
      +        """
      +
      +        if not self._client:
      +            self._client = digitalocean.Manager(token=self.token)
      +        return self._client
      +
      +    # Images
      +    @property
      +    def images(self):
      +        """Return a list of digital ocean availabe images
      +
      +        e.g
      +            dg.images  -> [<Image: 31354013 CentOS 6.9 x32>,
      +                                         <Image: 34902021 CentOS 6.9 x64>,...]
      +
      +        Returns
      +            List : list of images on digital ocean available
      +        """
      +        return self.client.get_distro_images()
      +
      +    @property
      +    def myimages(self):
      +        """Return a list of digital ocean images, you have created
      +
      +        e.g
      +            dg.myimages  -> [<Image: 48614453 Unknown Zero_OS>,
      +                                        <Image: 50898718 Ubuntu JumpScale>,...]
      +
      +        Returns
      +            List : list of images on digital ocean, you have created
      +        """
      +        return self.client.get_images(private=True)
      +
      +    @property
      +    def account_images(self):
      +        """Return a list of digital ocean images and the images you have created
      +
      +        e.g
      +            dg.account_images  -> [<Image: 31354013 CentOS 6.9 x32>,
      +                                             <Image: 34902021 CentOS 6.9 x64>,...]
      +
      +        Returns
      +            List : list of images on digital ocean images and the images you have created
      +        """
      +
      +        return self.images + self.myimages
      +
      +    def get_image(self, name):
      +        """Return an image
      +
      +        e.g
      +            dg.get_image(name="CentOS")  -> <Image: 31354013 CentOS 6.9 x32>
      +
      +        Args
      +            name (str): name of the  required image
      +        Returns
      +            Image : list of images on digital ocean images and the images you have created
      +        """
      +        for item in self.account_images:
      +            if item.description:
      +                name_do1 = item.description.lower()
      +            else:
      +                name_do1 = ""
      +            name_do2 = item.distribution + " " + item.name
      +            print(f" - {name_do1}--{name_do2}")
      +            if name_do1.lower().find(name.lower()) != -1 or name_do2.lower().find(name.lower()) != -1:
      +                return item
      +        raise j.exceptions.Base("did not find image:%s" % name)
      +
      +    def get_image_names(self, name=""):
      +        """ Return all the image  or images with a specified name
      +         e.g
      +            dg.get_image_names()  -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602',...]
      +            dg.get_image_names("centos") -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602']
      +
      +        Args
      +            name (str): name of the  required image
      +        Returns
      +            Image : list of images
      +        """
      +        res = []
      +        name = name.lower()
      +        for item in self.images:
      +            if item.description:
      +                name_do = item.description.lower()
      +            else:
      +                name_do = item.distribution + " " + item.name
      +            if name_do.find(name) != -1:
      +                res.append(name_do)
      +        return res
      +
      +    # Size
      +
      +    @property
      +    def sizes(self):
      +        """Return a list sizes available on digital ocean
      +
      +        e.g
      +            dg.sizes -> [s-1vcpu-1gb, 512mb, s-1vcpu-2gb, 1gb, s-3vcpu-1gb,.....]
      +
      +        Returns
      +            List : list of sizes
      +        """
      +        return self.client.get_all_sizes()
      +
      +    # Regions
      +
      +    @property
      +    def regions(self):
      +        """Return a list regions available on digital ocean
      +
      +        e.g
      +            dg.regions  -> [<Region: nyc1 New York 1>, <Region: sgp1 Singapore 1>,...]
      +
      +        Returns
      +            List : list of regions
      +        """
      +        return self.client.get_all_regions()
      +
      +    @property
      +    def region_names(self):
      +        """Returns Digital Ocean regions
      +
      +        e.g
      +            dg.region_names  -> ['nyc1', 'sgp1', 'lon1', 'nyc3', 'ams3', 'fra1', 'tor1', 'sfo2', 'blr1']
      +
      +        Returns
      +            list : list of digital ocean regions
      +        """
      +        return [i.slug for i in self.regions]
      +
      +    def get_region(self, name):
      +        """
      +        Returns specific region
      +
      +        e.g
      +            dg.get_region(name = 'nyc1')  -> <Region: nyc1 New York 1>
      +
      +        Args
      +            name (str) : name of the required region
      +        Returns
      +            Region : the region with the name specified
      +        """
      +        for item in self.regions:
      +            if name == item.slug:
      +                return item
      +            if name == item.name:
      +                return item
      +        raise j.exceptions.Base("did not find region:%s" % name)
      +
      +    # SSHkeys
      +    @property
      +    def sshkeys(self):
      +        """
      +        Return list of sshkeys on digital ocean
      +
      +        e.g
      +            dg.sshkeys  -> [<SSHKey: 25882170 3bot_container_sandbox>,
      +                             <SSHKey: 27130645 Geert-root>,...]
      +
      +        Returns
      +            list : list of sshkeys
      +        """
      +
      +        return self.client.get_all_sshkeys()
      +
      +    def get_default_sshkey(self):
      +        """
      +        Return sshkey you have added to your Digital Ocean client
      +
      +        e.g
      +            dg.get_default_sshkey()  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +        Returns
      +            list : list of sshkeys
      +        """
      +        pubkeyonly = self.sshkey.public_key
      +        for item in self.sshkeys:
      +            if item.public_key.find(pubkeyonly) != -1:
      +                return item
      +        return None
      +
      +    def set_default_sshkey(self, default_sshkey):
      +        """
      +        Set sshkey you  Digital Ocean client
      +
      +        e.g
      +            dg.set_default_sshkey(ssh)  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +        Args
      +            default_sshkey (SSHKeyClient) : sshkey client you have created
      +        """
      +        self.sshkey = default_sshkey
      +
      +    def get_sshkey(self, name):
      +        """
      +        get sshkey from  Digital Ocean
      +
      +        e.g
      +            dg.get_sshkey("rafy@rafy-Inspiron-3576")   ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +        Args
      +            name (string) : sshkey name
      +
      +        Returns
      +            SSHKey : return the specified sshkey
      +        """
      +        for item in self.sshkeys:
      +            if name == item.name:
      +                return item
      +        raise j.exceptions.Base("did not find key:%s" % name)
      +
      +    def __str__(self):
      +        return "digital ocean client:%s" % self.name
      +
      +    __repr__ = __str__
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var account_images
      +
      +

      Return a list of digital ocean images and the images you have created

      +

      e.g +dg.account_images +-> [, +,…]

      +

      Returns +List : list of images on digital ocean images and the images you have created

      +
      + +Expand source code + +
      @property
      +def account_images(self):
      +    """Return a list of digital ocean images and the images you have created
      +
      +    e.g
      +        dg.account_images  -> [<Image: 31354013 CentOS 6.9 x32>,
      +                                         <Image: 34902021 CentOS 6.9 x64>,...]
      +
      +    Returns
      +        List : list of images on digital ocean images and the images you have created
      +    """
      +
      +    return self.images + self.myimages
      +
      +
      +
      var client
      +
      +

      Return a new client if it is not set or it will return the already existed one

      +

      e.g +dg.client +-> +Returns +Manager: client form digital ocean manager

      +
      + +Expand source code + +
      @property
      +def client(self):
      +    """Return a new client if it is not set or it will return the already existed one
      +
      +    e.g
      +        dg.client  -> <Manager>
      +    Returns
      +        Manager: client form digital ocean manager
      +    """
      +
      +    if not self._client:
      +        self._client = digitalocean.Manager(token=self.token)
      +    return self._client
      +
      +
      +
      var droplets
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var images
      +
      +

      Return a list of digital ocean availabe images

      +

      e.g +dg.images +-> [, +,…]

      +

      Returns +List : list of images on digital ocean available

      +
      + +Expand source code + +
      @property
      +def images(self):
      +    """Return a list of digital ocean availabe images
      +
      +    e.g
      +        dg.images  -> [<Image: 31354013 CentOS 6.9 x32>,
      +                                     <Image: 34902021 CentOS 6.9 x64>,...]
      +
      +    Returns
      +        List : list of images on digital ocean available
      +    """
      +    return self.client.get_distro_images()
      +
      +
      +
      var myimages
      +
      +

      Return a list of digital ocean images, you have created

      +

      e.g +dg.myimages +-> [, +,…]

      +

      Returns +List : list of images on digital ocean, you have created

      +
      + +Expand source code + +
      @property
      +def myimages(self):
      +    """Return a list of digital ocean images, you have created
      +
      +    e.g
      +        dg.myimages  -> [<Image: 48614453 Unknown Zero_OS>,
      +                                    <Image: 50898718 Ubuntu JumpScale>,...]
      +
      +    Returns
      +        List : list of images on digital ocean, you have created
      +    """
      +    return self.client.get_images(private=True)
      +
      +
      +
      var name
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var projects
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var region_names
      +
      +

      Returns Digital Ocean regions

      +

      e.g +dg.region_names +-> ['nyc1', 'sgp1', 'lon1', 'nyc3', 'ams3', 'fra1', 'tor1', 'sfo2', 'blr1']

      +

      Returns +list : list of digital ocean regions

      +
      + +Expand source code + +
      @property
      +def region_names(self):
      +    """Returns Digital Ocean regions
      +
      +    e.g
      +        dg.region_names  -> ['nyc1', 'sgp1', 'lon1', 'nyc3', 'ams3', 'fra1', 'tor1', 'sfo2', 'blr1']
      +
      +    Returns
      +        list : list of digital ocean regions
      +    """
      +    return [i.slug for i in self.regions]
      +
      +
      +
      var regions
      +
      +

      Return a list regions available on digital ocean

      +

      e.g +dg.regions +-> [, ,…]

      +

      Returns +List : list of regions

      +
      + +Expand source code + +
      @property
      +def regions(self):
      +    """Return a list regions available on digital ocean
      +
      +    e.g
      +        dg.regions  -> [<Region: nyc1 New York 1>, <Region: sgp1 Singapore 1>,...]
      +
      +    Returns
      +        List : list of regions
      +    """
      +    return self.client.get_all_regions()
      +
      +
      +
      var sizes
      +
      +

      Return a list sizes available on digital ocean

      +

      e.g +dg.sizes -> [s-1vcpu-1gb, 512mb, s-1vcpu-2gb, 1gb, s-3vcpu-1gb,.....]

      +

      Returns +List : list of sizes

      +
      + +Expand source code + +
      @property
      +def sizes(self):
      +    """Return a list sizes available on digital ocean
      +
      +    e.g
      +        dg.sizes -> [s-1vcpu-1gb, 512mb, s-1vcpu-2gb, 1gb, s-3vcpu-1gb,.....]
      +
      +    Returns
      +        List : list of sizes
      +    """
      +    return self.client.get_all_sizes()
      +
      +
      +
      var sshkeys
      +
      +

      Return list of sshkeys on digital ocean

      +

      e.g +dg.sshkeys +-> [, +,…]

      +

      Returns +list : list of sshkeys

      +
      + +Expand source code + +
      @property
      +def sshkeys(self):
      +    """
      +    Return list of sshkeys on digital ocean
      +
      +    e.g
      +        dg.sshkeys  -> [<SSHKey: 25882170 3bot_container_sandbox>,
      +                         <SSHKey: 27130645 Geert-root>,...]
      +
      +    Returns
      +        list : list of sshkeys
      +    """
      +
      +    return self.client.get_all_sshkeys()
      +
      +
      +
      var token
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def get_default_sshkey(self) +
      +
      +

      Return sshkey you have added to your Digital Ocean client

      +

      e.g +dg.get_default_sshkey() +-> +

      +

      Returns +list : list of sshkeys

      +
      + +Expand source code + +
      def get_default_sshkey(self):
      +    """
      +    Return sshkey you have added to your Digital Ocean client
      +
      +    e.g
      +        dg.get_default_sshkey()  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +    Returns
      +        list : list of sshkeys
      +    """
      +    pubkeyonly = self.sshkey.public_key
      +    for item in self.sshkeys:
      +        if item.public_key.find(pubkeyonly) != -1:
      +            return item
      +    return None
      +
      +
      +
      +def get_image(self, name) +
      +
      +

      Return an image

      +

      e.g +dg.get_image(name="CentOS") +->

      +

      Args +name (str): name of the +required image +Returns +Image : list of images on digital ocean images and the images you have created

      +
      + +Expand source code + +
      def get_image(self, name):
      +    """Return an image
      +
      +    e.g
      +        dg.get_image(name="CentOS")  -> <Image: 31354013 CentOS 6.9 x32>
      +
      +    Args
      +        name (str): name of the  required image
      +    Returns
      +        Image : list of images on digital ocean images and the images you have created
      +    """
      +    for item in self.account_images:
      +        if item.description:
      +            name_do1 = item.description.lower()
      +        else:
      +            name_do1 = ""
      +        name_do2 = item.distribution + " " + item.name
      +        print(f" - {name_do1}--{name_do2}")
      +        if name_do1.lower().find(name.lower()) != -1 or name_do2.lower().find(name.lower()) != -1:
      +            return item
      +    raise j.exceptions.Base("did not find image:%s" % name)
      +
      +
      +
      +def get_image_names(self, name='') +
      +
      +

      Return all the image +or images with a specified name +e.g +dg.get_image_names() +-> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602',…] +dg.get_image_names("centos") -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602']

      +

      Args +name (str): name of the +required image +Returns +Image : list of images

      +
      + +Expand source code + +
      def get_image_names(self, name=""):
      +    """ Return all the image  or images with a specified name
      +     e.g
      +        dg.get_image_names()  -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602',...]
      +        dg.get_image_names("centos") -> ['centos 6.9 x32 20180130', 'centos 6.9 x64 20180602']
      +
      +    Args
      +        name (str): name of the  required image
      +    Returns
      +        Image : list of images
      +    """
      +    res = []
      +    name = name.lower()
      +    for item in self.images:
      +        if item.description:
      +            name_do = item.description.lower()
      +        else:
      +            name_do = item.distribution + " " + item.name
      +        if name_do.find(name) != -1:
      +            res.append(name_do)
      +    return res
      +
      +
      +
      +def get_region(self, name) +
      +
      +

      Returns specific region

      +

      e.g +dg.get_region(name = 'nyc1') +->

      +

      Args +name (str) : name of the required region +Returns +Region : the region with the name specified

      +
      + +Expand source code + +
      def get_region(self, name):
      +    """
      +    Returns specific region
      +
      +    e.g
      +        dg.get_region(name = 'nyc1')  -> <Region: nyc1 New York 1>
      +
      +    Args
      +        name (str) : name of the required region
      +    Returns
      +        Region : the region with the name specified
      +    """
      +    for item in self.regions:
      +        if name == item.slug:
      +            return item
      +        if name == item.name:
      +            return item
      +    raise j.exceptions.Base("did not find region:%s" % name)
      +
      +
      +
      +def get_sshkey(self, name) +
      +
      +

      get sshkey from +Digital Ocean

      +

      e.g +dg.get_sshkey("rafy@rafy-Inspiron-3576") +-> +

      +

      Args +name (string) : sshkey name

      +

      Returns +SSHKey : return the specified sshkey

      +
      + +Expand source code + +
      def get_sshkey(self, name):
      +    """
      +    get sshkey from  Digital Ocean
      +
      +    e.g
      +        dg.get_sshkey("rafy@rafy-Inspiron-3576")   ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +    Args
      +        name (string) : sshkey name
      +
      +    Returns
      +        SSHKey : return the specified sshkey
      +    """
      +    for item in self.sshkeys:
      +        if name == item.name:
      +            return item
      +    raise j.exceptions.Base("did not find key:%s" % name)
      +
      +
      +
      +def set_default_sshkey(self, default_sshkey) +
      +
      +

      Set sshkey you +Digital Ocean client

      +

      e.g +dg.set_default_sshkey(ssh) +-> +

      +

      Args +default_sshkey (SSHKeyClient) : sshkey client you have created

      +
      + +Expand source code + +
      def set_default_sshkey(self, default_sshkey):
      +    """
      +    Set sshkey you  Digital Ocean client
      +
      +    e.g
      +        dg.set_default_sshkey(ssh)  ->  <SSHKey: 25589987 rafy@rafy-Inspiron-3576>
      +
      +    Args
      +        default_sshkey (SSHKeyClient) : sshkey client you have created
      +    """
      +    self.sshkey = default_sshkey
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class Droplet +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class Droplet(Client):
      +    do_name = fields.String()
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +
      +    def set_digital_ocean_name(self, name):
      +        """Set a name for your Droplet to be used on Digital Ocean
      +
      +        e.g
      +            droplet.set_digital_ocean_name("test-name")
      +
      +        Args:
      +            name (str): name to be used on digital ocean
      +        """
      +        self.do_name = name
      +
      +    def get_digital_ocean_name(self):
      +        """Get a name for the Droplet which is used on digital ocean
      +
      +        e.g
      +            droplet.get_digital_ocean_name()  ->  "test-name"
      +
      +        Returns
      +            str: name for the droplet which is used on digital ocean
      +        """
      +        return self.do_name
      +
      +    def deploy(
      +        self,
      +        sshkey=None,
      +        region="Amsterdam 3",
      +        image="ubuntu 18.04",
      +        size_slug="s-1vcpu-2gb",
      +        delete=True,
      +        project_name=None,
      +    ):
      +        """
      +        Deploy your Droplet to Digital Ocean
      +
      +        Args
      +            sshkey (string): sshkey name used on digital ocean (if not set it will use the default one which already loaded)
      +            region (string): region name to deploy to
      +            image (string): Image name to be used
      +            size_slug (string): size of the droplet  (s-1vcpu-2gb,s-6vcpu-16gb,gd-8vcpu-32gb)
      +            delete (bool): delete the droplet if it is already deployed on digital ocean
      +            project_name (string): project to add this droplet it. If not specified the default project will be used.
      +
      +        """
      +        project = None
      +        if project_name:
      +            project = self.parent.projects.get_project_exist_remote(project_name)
      +            if not project:
      +                raise j.exceptions.Input("could not find project with name:%s" % project_name)
      +
      +        # Get ssh
      +        if not sshkey:
      +            sshkey_do = self.parent.get_default_sshkey()
      +            if not sshkey_do:
      +                # means we did not find the sshkey on digital ocean yet, need to create
      +                sshkey = self.parent.sshkey
      +
      +                key = digitalocean.SSHKey(
      +                    token=self.parent.projects.parent_instance.token, name=sshkey.name, public_key=sshkey.public_key
      +                )
      +                key.create()
      +                sshkey_do = self.parent.get_default_sshkey()
      +            assert sshkey_do
      +            sshkey = sshkey_do.name
      +
      +        if self.parent.droplets.check_droplet_exist_remote(self.do_name):
      +            dr0 = self.parent.droplets.get_droplet_exist_remote(self.do_name)
      +            if delete:
      +                dr0.destroy()
      +            else:
      +                sshcl = j.clients.sshclient.get(name="do_%s" % self.do_name, host=dr0.ip_address, sshkey=sshkey)
      +                return dr0, sshcl
      +
      +        sshkey = self.parent.droplets.parent_instance.get_sshkey(sshkey)
      +        region = self.parent.droplets.parent_instance.get_region(region)
      +        imagedo = self.parent.droplets.parent_instance.get_image(image)
      +
      +        img_slug_or_id = imagedo.slug if imagedo.slug else imagedo.id
      +
      +        droplet = digitalocean.Droplet(
      +            token=self.parent.droplets.parent_instance.token,
      +            name=self.do_name,
      +            region=region.slug,
      +            image=img_slug_or_id,
      +            size_slug=size_slug,
      +            ssh_keys=[sshkey],
      +            backups=False,
      +        )
      +        droplet.create()
      +
      +        if project:
      +            project.assign_resources(["do:droplet:%s" % droplet.id])
      +
      +    def delete_remote(self):
      +        """Delete Droplet from digital ocean
      +
      +        e.g
      +            droplet.delete_remote()
      +
      +        """
      +        droplet = self.parent.droplets.get_droplet_exist_remote(self.do_name)
      +        droplet.destroy()
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var do_name
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def delete_remote(self) +
      +
      +

      Delete Droplet from digital ocean

      +

      e.g +droplet.delete_remote()

      +
      + +Expand source code + +
      def delete_remote(self):
      +    """Delete Droplet from digital ocean
      +
      +    e.g
      +        droplet.delete_remote()
      +
      +    """
      +    droplet = self.parent.droplets.get_droplet_exist_remote(self.do_name)
      +    droplet.destroy()
      +
      +
      +
      +def deploy(self, sshkey=None, region='Amsterdam 3', image='ubuntu 18.04', size_slug='s-1vcpu-2gb', delete=True, project_name=None) +
      +
      +

      Deploy your Droplet to Digital Ocean

      +

      Args +sshkey (string): sshkey name used on digital ocean (if not set it will use the default one which already loaded) +region (string): region name to deploy to +image (string): Image name to be used +size_slug (string): size of the droplet +(s-1vcpu-2gb,s-6vcpu-16gb,gd-8vcpu-32gb) +delete (bool): delete the droplet if it is already deployed on digital ocean +project_name (string): project to add this droplet it. If not specified the default project will be used.

      +
      + +Expand source code + +
      def deploy(
      +    self,
      +    sshkey=None,
      +    region="Amsterdam 3",
      +    image="ubuntu 18.04",
      +    size_slug="s-1vcpu-2gb",
      +    delete=True,
      +    project_name=None,
      +):
      +    """
      +    Deploy your Droplet to Digital Ocean
      +
      +    Args
      +        sshkey (string): sshkey name used on digital ocean (if not set it will use the default one which already loaded)
      +        region (string): region name to deploy to
      +        image (string): Image name to be used
      +        size_slug (string): size of the droplet  (s-1vcpu-2gb,s-6vcpu-16gb,gd-8vcpu-32gb)
      +        delete (bool): delete the droplet if it is already deployed on digital ocean
      +        project_name (string): project to add this droplet it. If not specified the default project will be used.
      +
      +    """
      +    project = None
      +    if project_name:
      +        project = self.parent.projects.get_project_exist_remote(project_name)
      +        if not project:
      +            raise j.exceptions.Input("could not find project with name:%s" % project_name)
      +
      +    # Get ssh
      +    if not sshkey:
      +        sshkey_do = self.parent.get_default_sshkey()
      +        if not sshkey_do:
      +            # means we did not find the sshkey on digital ocean yet, need to create
      +            sshkey = self.parent.sshkey
      +
      +            key = digitalocean.SSHKey(
      +                token=self.parent.projects.parent_instance.token, name=sshkey.name, public_key=sshkey.public_key
      +            )
      +            key.create()
      +            sshkey_do = self.parent.get_default_sshkey()
      +        assert sshkey_do
      +        sshkey = sshkey_do.name
      +
      +    if self.parent.droplets.check_droplet_exist_remote(self.do_name):
      +        dr0 = self.parent.droplets.get_droplet_exist_remote(self.do_name)
      +        if delete:
      +            dr0.destroy()
      +        else:
      +            sshcl = j.clients.sshclient.get(name="do_%s" % self.do_name, host=dr0.ip_address, sshkey=sshkey)
      +            return dr0, sshcl
      +
      +    sshkey = self.parent.droplets.parent_instance.get_sshkey(sshkey)
      +    region = self.parent.droplets.parent_instance.get_region(region)
      +    imagedo = self.parent.droplets.parent_instance.get_image(image)
      +
      +    img_slug_or_id = imagedo.slug if imagedo.slug else imagedo.id
      +
      +    droplet = digitalocean.Droplet(
      +        token=self.parent.droplets.parent_instance.token,
      +        name=self.do_name,
      +        region=region.slug,
      +        image=img_slug_or_id,
      +        size_slug=size_slug,
      +        ssh_keys=[sshkey],
      +        backups=False,
      +    )
      +    droplet.create()
      +
      +    if project:
      +        project.assign_resources(["do:droplet:%s" % droplet.id])
      +
      +
      +
      +def get_digital_ocean_name(self) +
      +
      +

      Get a name for the Droplet which is used on digital ocean

      +

      e.g +droplet.get_digital_ocean_name() +-> +"test-name"

      +

      Returns +str: name for the droplet which is used on digital ocean

      +
      + +Expand source code + +
      def get_digital_ocean_name(self):
      +    """Get a name for the Droplet which is used on digital ocean
      +
      +    e.g
      +        droplet.get_digital_ocean_name()  ->  "test-name"
      +
      +    Returns
      +        str: name for the droplet which is used on digital ocean
      +    """
      +    return self.do_name
      +
      +
      +
      +def set_digital_ocean_name(self, name) +
      +
      +

      Set a name for your Droplet to be used on Digital Ocean

      +

      e.g +droplet.set_digital_ocean_name("test-name")

      +

      Args

      +
      +
      name : str
      +
      name to be used on digital ocean
      +
      +
      + +Expand source code + +
      def set_digital_ocean_name(self, name):
      +    """Set a name for your Droplet to be used on Digital Ocean
      +
      +    e.g
      +        droplet.set_digital_ocean_name("test-name")
      +
      +    Args:
      +        name (str): name to be used on digital ocean
      +    """
      +    self.do_name = name
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class DropletFactory +(*args, **kwargs) +
      +
      +

      Stored factories are a custom type of Factory, which uses current configured store backend +to store all instance configurations.

      +

      get a new stored factory given the type to create and store instances for.

      +

      Any factory can have a name, parent Base instance and a parent factory.

      +

      Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

      +

      Args

      +
      +
      type_ : Base
      +
      Base class type
      +
      name_ : str, optional
      +
      factory name. Defaults to None.
      +
      parent_instance_ : Base, optional
      +
      a parent Base instance. Defaults to None.
      +
      parent_factory_ : Factory, optional
      +
      a parent Factory. Defaults to None.
      +
      +
      + +Expand source code + +
      class DropletFactory(StoredFactory):
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +
      +    def list_remote(self, project_name=None):
      +        """
      +        List all remote droplet or list droplets for a project if it is specified
      +
      +        e.g
      +            dg.droplets.list_remote()  -> list of droplets
      +            dg.droplets.list_remote("codescalers")  -> list of droplets on codescalers project
      +
      +        Args:
      +            project_name (str) : name of project on digital ocean (optional)
      +
      +        Returns
      +            list (Droplet) : list of droplets on digital ocean
      +
      +
      +        """
      +        if project_name:
      +            project = self.parent_instance.projects.get_project_exist_remote(project_name)
      +            return project.list_droplets()
      +
      +        return self.parent_instance.client.get_all_droplets()
      +
      +    def check_droplet_exist_remote(self, name):
      +        """
      +        Check droplet exists on digital ocean
      +
      +        e.g
      +            dg.droplets.check_droplet_exist_remote("3git")  -> True
      +            dg.droplets.check_droplet_exist_remote("sdfgdfed")  -> False
      +
      +        Args:
      +            name (str) : name of droplet
      +
      +        Returns
      +            bool : True if the droplet exist or False if the droplet does not exist
      +        """
      +        for droplet in self.list_remote():
      +            if droplet.name.lower() == name.lower():
      +                return True
      +        return False
      +
      +    def get_droplet_exist_remote(self, name):
      +        """
      +        Get Droplet exists from Digital Ocean
      +
      +        e.g
      +            dg.droplets.get_droplet_exist_remote("3git")
      +
      +        Args:
      +            name (str) : name of droplet
      +
      +        Returns
      +            droplet : droplet with the name specified
      +
      +        """
      +        for droplet in self.list_remote():
      +            if droplet.name.lower() == name.lower():
      +                return droplet
      +        raise j.exceptions.Input("could not find droplet with name:%s on your Digital Ocean account" % name)
      +
      +    def shutdown_all(self, project_name=None):
      +        """
      +        Shutdown all the droplets or droplets in specific project
      +
      +        e.g
      +            dg.droplets.shutdown_all("codescalers")
      +            dg.droplets.shutdown_all()
      +
      +        Args:
      +            name (str) : name of the project
      +
      +        """
      +        for droplet in self.list_remote(project_name):
      +            droplet.shutdown()
      +
      +    def delete_all(self, ignore=None, interactive=True, project_name=None):
      +        """
      +        Delete all the droplets or delete all the droplets in specific project
      +
      +        e.g
      +            dg.droplets.delete_all(project_name = "codescalers")
      +            dg.droplets.delete_all()
      +
      +        Args:
      +            project_name (str) : name of the project
      +            ignore (list): list of ignored droplets to prevent their deletion
      +            interactive (bool): if True the deletion will be interactive and
      +                                confirm if you want to delete but if False it
      +                                will delete directly
      +        """
      +        if not ignore:
      +            ignore = []
      +
      +        def test(ignore, name):
      +            if name.startswith("TF-"):
      +                return False
      +            for item in ignore:
      +                if name.lower().find(item.lower()) != -1:
      +                    return False
      +            return True
      +
      +        todo = []
      +        for droplet in self.list_remote(project_name):
      +            if test(ignore, droplet.name):
      +                todo.append(droplet)
      +        if todo != []:
      +            todotxt = ",".join([i.name for i in todo])
      +            if not interactive or j.tools.console.ask_yes_no("ok to delete:%s" % todotxt):
      +                for droplet in todo:
      +                    droplet.destroy()
      +
      +

      Ancestors

      + +

      Methods

      +
      +
      +def check_droplet_exist_remote(self, name) +
      +
      +

      Check droplet exists on digital ocean

      +

      e.g +dg.droplets.check_droplet_exist_remote("3git") +-> True +dg.droplets.check_droplet_exist_remote("sdfgdfed") +-> False

      +

      Args

      +

      name (str) : name of droplet +Returns +bool : True if the droplet exist or False if the droplet does not exist

      +
      + +Expand source code + +
      def check_droplet_exist_remote(self, name):
      +    """
      +    Check droplet exists on digital ocean
      +
      +    e.g
      +        dg.droplets.check_droplet_exist_remote("3git")  -> True
      +        dg.droplets.check_droplet_exist_remote("sdfgdfed")  -> False
      +
      +    Args:
      +        name (str) : name of droplet
      +
      +    Returns
      +        bool : True if the droplet exist or False if the droplet does not exist
      +    """
      +    for droplet in self.list_remote():
      +        if droplet.name.lower() == name.lower():
      +            return True
      +    return False
      +
      +
      +
      +def delete_all(self, ignore=None, interactive=True, project_name=None) +
      +
      +

      Delete all the droplets or delete all the droplets in specific project

      +

      e.g +dg.droplets.delete_all(project_name = "codescalers") +dg.droplets.delete_all()

      +

      Args

      +
      +
      project_name (str) : name of the project
      +
      ignore : list
      +
      list of ignored droplets to prevent their deletion
      +
      interactive : bool
      +
      if True the deletion will be interactive and +confirm if you want to delete but if False it +will delete directly
      +
      +
      + +Expand source code + +
      def delete_all(self, ignore=None, interactive=True, project_name=None):
      +    """
      +    Delete all the droplets or delete all the droplets in specific project
      +
      +    e.g
      +        dg.droplets.delete_all(project_name = "codescalers")
      +        dg.droplets.delete_all()
      +
      +    Args:
      +        project_name (str) : name of the project
      +        ignore (list): list of ignored droplets to prevent their deletion
      +        interactive (bool): if True the deletion will be interactive and
      +                            confirm if you want to delete but if False it
      +                            will delete directly
      +    """
      +    if not ignore:
      +        ignore = []
      +
      +    def test(ignore, name):
      +        if name.startswith("TF-"):
      +            return False
      +        for item in ignore:
      +            if name.lower().find(item.lower()) != -1:
      +                return False
      +        return True
      +
      +    todo = []
      +    for droplet in self.list_remote(project_name):
      +        if test(ignore, droplet.name):
      +            todo.append(droplet)
      +    if todo != []:
      +        todotxt = ",".join([i.name for i in todo])
      +        if not interactive or j.tools.console.ask_yes_no("ok to delete:%s" % todotxt):
      +            for droplet in todo:
      +                droplet.destroy()
      +
      +
      +
      +def get_droplet_exist_remote(self, name) +
      +
      +

      Get Droplet exists from Digital Ocean

      +

      e.g +dg.droplets.get_droplet_exist_remote("3git")

      +

      Args

      +

      name (str) : name of droplet +Returns +droplet : droplet with the name specified

      +
      + +Expand source code + +
      def get_droplet_exist_remote(self, name):
      +    """
      +    Get Droplet exists from Digital Ocean
      +
      +    e.g
      +        dg.droplets.get_droplet_exist_remote("3git")
      +
      +    Args:
      +        name (str) : name of droplet
      +
      +    Returns
      +        droplet : droplet with the name specified
      +
      +    """
      +    for droplet in self.list_remote():
      +        if droplet.name.lower() == name.lower():
      +            return droplet
      +    raise j.exceptions.Input("could not find droplet with name:%s on your Digital Ocean account" % name)
      +
      +
      +
      +def list_remote(self, project_name=None) +
      +
      +

      List all remote droplet or list droplets for a project if it is specified

      +

      e.g +dg.droplets.list_remote() +-> list of droplets +dg.droplets.list_remote("codescalers") +-> list of droplets on codescalers project

      +

      Args

      +

      project_name (str) : name of project on digital ocean (optional) +Returns +list (Droplet) : list of droplets on digital ocean

      +
      + +Expand source code + +
      def list_remote(self, project_name=None):
      +    """
      +    List all remote droplet or list droplets for a project if it is specified
      +
      +    e.g
      +        dg.droplets.list_remote()  -> list of droplets
      +        dg.droplets.list_remote("codescalers")  -> list of droplets on codescalers project
      +
      +    Args:
      +        project_name (str) : name of project on digital ocean (optional)
      +
      +    Returns
      +        list (Droplet) : list of droplets on digital ocean
      +
      +
      +    """
      +    if project_name:
      +        project = self.parent_instance.projects.get_project_exist_remote(project_name)
      +        return project.list_droplets()
      +
      +    return self.parent_instance.client.get_all_droplets()
      +
      +
      +
      +def shutdown_all(self, project_name=None) +
      +
      +

      Shutdown all the droplets or droplets in specific project

      +

      e.g +dg.droplets.shutdown_all("codescalers") +dg.droplets.shutdown_all()

      +

      Args

      +

      name (str) : name of the project

      +
      + +Expand source code + +
      def shutdown_all(self, project_name=None):
      +    """
      +    Shutdown all the droplets or droplets in specific project
      +
      +    e.g
      +        dg.droplets.shutdown_all("codescalers")
      +        dg.droplets.shutdown_all()
      +
      +    Args:
      +        name (str) : name of the project
      +
      +    """
      +    for droplet in self.list_remote(project_name):
      +        droplet.shutdown()
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class Project +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class Project(Client):
      +    do_name = fields.String()
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +
      +    def set_digital_ocean_name(self, name):
      +        """Set a name for your project to be used on Digital Ocean
      +        e.g
      +            project.set_digital_ocean_name("test project DO client")
      +
      +        Args:
      +            name (str): name to be used on digital ocean
      +        """
      +        self.do_name = name
      +
      +    def get_digital_ocean_name(self):
      +        """Get a name for the project which is used on digital ocean
      +        e.g
      +            project.get_digital_ocean_name()  ->  "test project DO client"
      +
      +        Returns:
      +            str: name for the project which is used on digital ocean
      +        """
      +        return self.do_name
      +
      +    def deploy(self, purpose, description="", environment="", is_default=False):
      +        """Create a digital ocean project
      +        e.g
      +            project.deploy(purpose="testing digital ocean client")  -> project
      +        Args:
      +            purpose(str): purpose of the project (not optional)
      +            description(str): description of the project, defaults to ""
      +            environment(str): environment of project's resources, defaults to ""
      +            is_default(bool): make this the default project for your user
      +
      +        Returns:
      +            project: The project object that has been created
      +        """
      +
      +        if self.parent.projects.check_project_exist_remote(self.do_name):
      +            raise j.exceptions.Value("A project with the same name already exists")
      +
      +        project = ProjectManagement(
      +            token=self.parent.projects.parent_instance.token,
      +            name=self.do_name,
      +            purpose=purpose,
      +            description=description,
      +            environment=environment,
      +            is_default=is_default,
      +        )
      +        project.create()
      +
      +        if is_default:
      +            project.update(is_default=True)
      +
      +        return project
      +
      +    def delete_remote(self):
      +        """Delete the project from Digital Ocean (A project can't be deleted unless it has no resources.)
      +
      +        e.g
      +            project.delete_remote()
      +        """
      +        project = self.parent.projects.get_project_exist_remote(self.do_name)
      +        project.delete()
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var do_name
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def delete_remote(self) +
      +
      +

      Delete the project from Digital Ocean (A project can't be deleted unless it has no resources.)

      +

      e.g +project.delete_remote()

      +
      + +Expand source code + +
      def delete_remote(self):
      +    """Delete the project from Digital Ocean (A project can't be deleted unless it has no resources.)
      +
      +    e.g
      +        project.delete_remote()
      +    """
      +    project = self.parent.projects.get_project_exist_remote(self.do_name)
      +    project.delete()
      +
      +
      +
      +def deploy(self, purpose, description='', environment='', is_default=False) +
      +
      +

      Create a digital ocean project +e.g +project.deploy(purpose="testing digital ocean client") +-> project

      +

      Args

      +

      purpose(str): purpose of the project (not optional) +description(str): description of the project, defaults to "" +environment(str): environment of project's resources, defaults to "" +is_default(bool): make this the default project for your user

      +

      Returns

      +
      +
      project
      +
      The project object that has been created
      +
      +
      + +Expand source code + +
      def deploy(self, purpose, description="", environment="", is_default=False):
      +    """Create a digital ocean project
      +    e.g
      +        project.deploy(purpose="testing digital ocean client")  -> project
      +    Args:
      +        purpose(str): purpose of the project (not optional)
      +        description(str): description of the project, defaults to ""
      +        environment(str): environment of project's resources, defaults to ""
      +        is_default(bool): make this the default project for your user
      +
      +    Returns:
      +        project: The project object that has been created
      +    """
      +
      +    if self.parent.projects.check_project_exist_remote(self.do_name):
      +        raise j.exceptions.Value("A project with the same name already exists")
      +
      +    project = ProjectManagement(
      +        token=self.parent.projects.parent_instance.token,
      +        name=self.do_name,
      +        purpose=purpose,
      +        description=description,
      +        environment=environment,
      +        is_default=is_default,
      +    )
      +    project.create()
      +
      +    if is_default:
      +        project.update(is_default=True)
      +
      +    return project
      +
      +
      +
      +def get_digital_ocean_name(self) +
      +
      +

      Get a name for the project which is used on digital ocean +e.g +project.get_digital_ocean_name() +-> +"test project DO client"

      +

      Returns

      +
      +
      str
      +
      name for the project which is used on digital ocean
      +
      +
      + +Expand source code + +
      def get_digital_ocean_name(self):
      +    """Get a name for the project which is used on digital ocean
      +    e.g
      +        project.get_digital_ocean_name()  ->  "test project DO client"
      +
      +    Returns:
      +        str: name for the project which is used on digital ocean
      +    """
      +    return self.do_name
      +
      +
      +
      +def set_digital_ocean_name(self, name) +
      +
      +

      Set a name for your project to be used on Digital Ocean +e.g +project.set_digital_ocean_name("test project DO client")

      +

      Args

      +
      +
      name : str
      +
      name to be used on digital ocean
      +
      +
      + +Expand source code + +
      def set_digital_ocean_name(self, name):
      +    """Set a name for your project to be used on Digital Ocean
      +    e.g
      +        project.set_digital_ocean_name("test project DO client")
      +
      +    Args:
      +        name (str): name to be used on digital ocean
      +    """
      +    self.do_name = name
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class ProjectFactory +(*args, **kwargs) +
      +
      +

      Stored factories are a custom type of Factory, which uses current configured store backend +to store all instance configurations.

      +

      get a new stored factory given the type to create and store instances for.

      +

      Any factory can have a name, parent Base instance and a parent factory.

      +

      Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

      +

      Args

      +
      +
      type_ : Base
      +
      Base class type
      +
      name_ : str, optional
      +
      factory name. Defaults to None.
      +
      parent_instance_ : Base, optional
      +
      a parent Base instance. Defaults to None.
      +
      parent_factory_ : Factory, optional
      +
      a parent Factory. Defaults to None.
      +
      +
      + +Expand source code + +
      class ProjectFactory(StoredFactory):
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +
      +    def list_remote(self):
      +        """
      +        Returns list of projects on Digital Ocean
      +
      +        e.g
      +            dg.projects.list_remote()  -> list of projects
      +
      +        Returns
      +            list(projects): list of projects on digital ocean
      +
      +        """
      +        return ProjectManagement.list(self.parent_instance.client)
      +
      +    def check_project_exist_remote(self, name):
      +        """
      +        Check a project with specific name exits on Digital Ocean
      +
      +        e.g
      +            dg.projects.check_project_exist_remote("codescalers")  -> True
      +            dg.projects.check_project_exist_remote("dsrfjsdfjl")  -> False
      +
      +        Args
      +            name (str): name of the project
      +
      +        Returns
      +            bool : True if the project exits and False if the project does not exist on digital ocean
      +        """
      +        for project in self.list_remote():
      +            if project.name == name:
      +                return True
      +        return False
      +
      +    def get_project_exist_remote(self, name):
      +        """
      +        Get a project with specifc name from  Digital Ocean.
      +
      +        e.g
      +            dg.projects.get_project_exist_remote("codescalers")  -> project
      +
      +        Args
      +            name (str): name of the project
      +
      +        Returns
      +            Project : a project from digital ocean with the name specified
      +        """
      +        for project in self.list_remote():
      +            if project.name == name:
      +                return project
      +        raise j.exceptions.Input("could not find project with name:%s on your Digital Ocean account" % name)
      +
      +

      Ancestors

      + +

      Methods

      +
      +
      +def check_project_exist_remote(self, name) +
      +
      +

      Check a project with specific name exits on Digital Ocean

      +

      e.g +dg.projects.check_project_exist_remote("codescalers") +-> True +dg.projects.check_project_exist_remote("dsrfjsdfjl") +-> False

      +

      Args +name (str): name of the project

      +

      Returns +bool : True if the project exits and False if the project does not exist on digital ocean

      +
      + +Expand source code + +
      def check_project_exist_remote(self, name):
      +    """
      +    Check a project with specific name exits on Digital Ocean
      +
      +    e.g
      +        dg.projects.check_project_exist_remote("codescalers")  -> True
      +        dg.projects.check_project_exist_remote("dsrfjsdfjl")  -> False
      +
      +    Args
      +        name (str): name of the project
      +
      +    Returns
      +        bool : True if the project exits and False if the project does not exist on digital ocean
      +    """
      +    for project in self.list_remote():
      +        if project.name == name:
      +            return True
      +    return False
      +
      +
      +
      +def get_project_exist_remote(self, name) +
      +
      +

      Get a project with specifc name from +Digital Ocean.

      +

      e.g +dg.projects.get_project_exist_remote("codescalers") +-> project

      +

      Args +name (str): name of the project

      +

      Returns +Project : a project from digital ocean with the name specified

      +
      + +Expand source code + +
      def get_project_exist_remote(self, name):
      +    """
      +    Get a project with specifc name from  Digital Ocean.
      +
      +    e.g
      +        dg.projects.get_project_exist_remote("codescalers")  -> project
      +
      +    Args
      +        name (str): name of the project
      +
      +    Returns
      +        Project : a project from digital ocean with the name specified
      +    """
      +    for project in self.list_remote():
      +        if project.name == name:
      +            return project
      +    raise j.exceptions.Input("could not find project with name:%s on your Digital Ocean account" % name)
      +
      +
      +
      +def list_remote(self) +
      +
      +

      Returns list of projects on Digital Ocean

      +

      e.g +dg.projects.list_remote() +-> list of projects

      +

      Returns +list(projects): list of projects on digital ocean

      +
      + +Expand source code + +
      def list_remote(self):
      +    """
      +    Returns list of projects on Digital Ocean
      +
      +    e.g
      +        dg.projects.list_remote()  -> list of projects
      +
      +    Returns
      +        list(projects): list of projects on digital ocean
      +
      +    """
      +    return ProjectManagement.list(self.parent_instance.client)
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/digitalocean/index.html b/docs/api/jumpscale/clients/digitalocean/index.html new file mode 100644 index 000000000..dac5e75ca --- /dev/null +++ b/docs/api/jumpscale/clients/digitalocean/index.html @@ -0,0 +1,108 @@ + + + + + + +jumpscale.clients.digitalocean API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.digitalocean

      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +
      +    from jumpscale.core.base import StoredFactory
      +
      +    from .digitalocean import DigitalOcean
      +
      +    return StoredFactory(DigitalOcean)
      +
      +
      +
      +

      Sub-modules

      +
      +
      jumpscale.clients.digitalocean.digitalocean
      +
      +

      This module is used to manage your digital ocean account, create droplet,list all the droplets, destroy droplets, create project, list all the …

      +
      +
      jumpscale.clients.digitalocean.project
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +
      +    from jumpscale.core.base import StoredFactory
      +
      +    from .digitalocean import DigitalOcean
      +
      +    return StoredFactory(DigitalOcean)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/digitalocean/project.html b/docs/api/jumpscale/clients/digitalocean/project.html new file mode 100644 index 000000000..e1fa6ef49 --- /dev/null +++ b/docs/api/jumpscale/clients/digitalocean/project.html @@ -0,0 +1,687 @@ + + + + + + +jumpscale.clients.digitalocean.project API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.digitalocean.project

      +
      +
      +
      + +Expand source code + +
      from digitalocean import Droplet
      +from digitalocean.baseapi import BaseAPI, Error, GET, POST, DELETE, PUT
      +
      +
      +class ProjectManagement(BaseAPI):
      +    """Project management
      +
      +    Attributes accepted at creation time:
      +
      +    Args:
      +        name (str): project name
      +        description (str): project size
      +        purpose (str): purpose of the project
      +        environemnt (str): environment of the project's resources
      +
      +    Attributes returned by API:
      +        * id (int): project id
      +        * owner_uuid (str): uuid of the project owner
      +        * owner_id (str): id of the project owner
      +        * name (str): project name
      +        * description (str): project description
      +        * purpose (str): project purpose
      +        * environment (str): environment of the project's resources
      +        * is_default (bool): If true, all resources will be added to this project if no project is specified.
      +        * created_at (str): creation date in format u'2014-11-06T10:42:09Z'
      +        * updated_at (str): update date in format u'2014-11-06T10:42:09Z'
      +
      +    """
      +
      +    def __init__(self, *args, **kwargs):
      +        # Defining default values
      +        self.id = None
      +        self.name = None
      +        self.owner_uuid = None
      +        self.owner_id = None
      +        self.description = None
      +        self.purpose = None
      +        self.environment = None
      +        self.is_default = False
      +        self.updated_at = None
      +        self.created_at = None
      +
      +        # This will load also the values passed
      +        super(ProjectManagement, self).__init__(*args, **kwargs)
      +
      +    @classmethod
      +    def get_object(cls, api_token, project_id):
      +        """Class method that will return a Project object by ID.
      +
      +        Args:
      +            api_token (str): token
      +            project_id (int): project id
      +        """
      +        project = cls(token=api_token, id=project_id)
      +        project.load()
      +        return project
      +
      +    @classmethod
      +    def list(cls, client):
      +
      +        data = client.get_data("projects")
      +
      +        projects = list()
      +        for jsoned in data["projects"]:
      +            project = cls(**jsoned)
      +            project.token = client.token
      +
      +            projects.append(project)
      +
      +        return projects
      +
      +    def load(self):
      +        """
      +        Fetch data about project - use this instead of get_data()
      +        """
      +        projects = self.get_data("projects/%s" % self.id)
      +        project = projects["project"]
      +
      +        for attr in project.keys():
      +            setattr(self, attr, project[attr])
      +
      +        return self
      +
      +    def _update_data(self, project):
      +        self.id = project["id"]
      +        self.owner_uuid = project["owner_uuid"]
      +        self.owner_id = project["owner_id"]
      +        self.name = project["name"]
      +        self.description = project["description"]
      +        self.purpose = project["purpose"]
      +        self.environment = project["environment"]
      +        self.is_default = project["is_default"]
      +        self.created_at = project["created_at"]
      +        self.updated_at = project["updated_at"]
      +
      +    def create(self, *args, **kwargs):
      +        """
      +        Create the project with object properties.
      +
      +        Note: Every argument and parameter given to this method will be
      +        assigned to the object.
      +        """
      +        for attr in kwargs.keys():
      +            setattr(self, attr, kwargs[attr])
      +
      +        data = {
      +            "name": self.name,
      +            "description": self.description,
      +            "purpose": self.purpose,
      +            "environment": self.environment,
      +        }
      +
      +        data = self.get_data("projects", type=POST, params=data)
      +        self._update_data(data["project"])
      +
      +    def update(self, *args, **kwargs):
      +        """
      +        Update the project with object properties.
      +
      +        Note: Every argument and parameter given to this method will be
      +        assigned to the object.
      +        """
      +        for attr in kwargs.keys():
      +            setattr(self, attr, kwargs[attr])
      +
      +        data = {
      +            "name": self.name,
      +            "description": self.description,
      +            "purpose": self.purpose,
      +            "environment": self.environment,
      +            "is_default": self.is_default,
      +        }
      +
      +        data = self.get_data("projects/%s" % self.id, type=PUT, params=data)
      +        self._update_data(data["project"])
      +
      +    def delete(self):
      +        """
      +        Delete the project.
      +        To be deleted, a project must not have any resources assigned to it. Any existing resources must first be reassigned or destroyed.
      +        """
      +        self.get_data("projects/%s" % self.id, type=DELETE)
      +
      +    def list_resources(self):
      +        """
      +        List all resources in the project
      +        """
      +        return self.get_data("projects/%s/resources" % self.id)["resources"]
      +
      +    def list_droplets(self):
      +        """
      +        List all droplets in the project
      +        """
      +        resources = self.list_resources()
      +        droplets = []
      +        for resource in resources:
      +            if not resource["urn"].startswith("do:droplet:"):
      +                continue
      +            droplet_id = resource["urn"].replace("do:droplet:", "")
      +            droplet = Droplet.get_object(api_token=self.token, droplet_id=droplet_id)
      +            droplets.append(droplet)
      +
      +        return droplets
      +
      +    def assign_resources(self, resources):
      +        """Assign resources to the project.
      +
      +        :param resources: A list of uniform resource names (URNs) to be added to a project.
      +        :type resources: [str]
      +        """
      +        self.get_data("projects/%s/resources" % self.id, type=POST, params={"resources": resources})
      +
      +    def __str__(self):
      +        return "<Project: %s %s>" % (self.id, self.name)
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class ProjectManagement +(*args, **kwargs) +
      +
      +

      Project management

      +

      Attributes accepted at creation time:

      +

      Args

      +
      +
      name : str
      +
      project name
      +
      description : str
      +
      project size
      +
      purpose : str
      +
      purpose of the project
      +
      environemnt : str
      +
      environment of the project's resources
      +
      +

      Attributes returned by API: +* id (int): project id +* owner_uuid (str): uuid of the project owner +* owner_id (str): id of the project owner +* name (str): project name +* description (str): project description +* purpose (str): project purpose +* environment (str): environment of the project's resources +* is_default (bool): If true, all resources will be added to this project if no project is specified. +* created_at (str): creation date in format u'2014-11-06T10:42:09Z' +* updated_at (str): update date in format u'2014-11-06T10:42:09Z'

      +
      + +Expand source code + +
      class ProjectManagement(BaseAPI):
      +    """Project management
      +
      +    Attributes accepted at creation time:
      +
      +    Args:
      +        name (str): project name
      +        description (str): project size
      +        purpose (str): purpose of the project
      +        environemnt (str): environment of the project's resources
      +
      +    Attributes returned by API:
      +        * id (int): project id
      +        * owner_uuid (str): uuid of the project owner
      +        * owner_id (str): id of the project owner
      +        * name (str): project name
      +        * description (str): project description
      +        * purpose (str): project purpose
      +        * environment (str): environment of the project's resources
      +        * is_default (bool): If true, all resources will be added to this project if no project is specified.
      +        * created_at (str): creation date in format u'2014-11-06T10:42:09Z'
      +        * updated_at (str): update date in format u'2014-11-06T10:42:09Z'
      +
      +    """
      +
      +    def __init__(self, *args, **kwargs):
      +        # Defining default values
      +        self.id = None
      +        self.name = None
      +        self.owner_uuid = None
      +        self.owner_id = None
      +        self.description = None
      +        self.purpose = None
      +        self.environment = None
      +        self.is_default = False
      +        self.updated_at = None
      +        self.created_at = None
      +
      +        # This will load also the values passed
      +        super(ProjectManagement, self).__init__(*args, **kwargs)
      +
      +    @classmethod
      +    def get_object(cls, api_token, project_id):
      +        """Class method that will return a Project object by ID.
      +
      +        Args:
      +            api_token (str): token
      +            project_id (int): project id
      +        """
      +        project = cls(token=api_token, id=project_id)
      +        project.load()
      +        return project
      +
      +    @classmethod
      +    def list(cls, client):
      +
      +        data = client.get_data("projects")
      +
      +        projects = list()
      +        for jsoned in data["projects"]:
      +            project = cls(**jsoned)
      +            project.token = client.token
      +
      +            projects.append(project)
      +
      +        return projects
      +
      +    def load(self):
      +        """
      +        Fetch data about project - use this instead of get_data()
      +        """
      +        projects = self.get_data("projects/%s" % self.id)
      +        project = projects["project"]
      +
      +        for attr in project.keys():
      +            setattr(self, attr, project[attr])
      +
      +        return self
      +
      +    def _update_data(self, project):
      +        self.id = project["id"]
      +        self.owner_uuid = project["owner_uuid"]
      +        self.owner_id = project["owner_id"]
      +        self.name = project["name"]
      +        self.description = project["description"]
      +        self.purpose = project["purpose"]
      +        self.environment = project["environment"]
      +        self.is_default = project["is_default"]
      +        self.created_at = project["created_at"]
      +        self.updated_at = project["updated_at"]
      +
      +    def create(self, *args, **kwargs):
      +        """
      +        Create the project with object properties.
      +
      +        Note: Every argument and parameter given to this method will be
      +        assigned to the object.
      +        """
      +        for attr in kwargs.keys():
      +            setattr(self, attr, kwargs[attr])
      +
      +        data = {
      +            "name": self.name,
      +            "description": self.description,
      +            "purpose": self.purpose,
      +            "environment": self.environment,
      +        }
      +
      +        data = self.get_data("projects", type=POST, params=data)
      +        self._update_data(data["project"])
      +
      +    def update(self, *args, **kwargs):
      +        """
      +        Update the project with object properties.
      +
      +        Note: Every argument and parameter given to this method will be
      +        assigned to the object.
      +        """
      +        for attr in kwargs.keys():
      +            setattr(self, attr, kwargs[attr])
      +
      +        data = {
      +            "name": self.name,
      +            "description": self.description,
      +            "purpose": self.purpose,
      +            "environment": self.environment,
      +            "is_default": self.is_default,
      +        }
      +
      +        data = self.get_data("projects/%s" % self.id, type=PUT, params=data)
      +        self._update_data(data["project"])
      +
      +    def delete(self):
      +        """
      +        Delete the project.
      +        To be deleted, a project must not have any resources assigned to it. Any existing resources must first be reassigned or destroyed.
      +        """
      +        self.get_data("projects/%s" % self.id, type=DELETE)
      +
      +    def list_resources(self):
      +        """
      +        List all resources in the project
      +        """
      +        return self.get_data("projects/%s/resources" % self.id)["resources"]
      +
      +    def list_droplets(self):
      +        """
      +        List all droplets in the project
      +        """
      +        resources = self.list_resources()
      +        droplets = []
      +        for resource in resources:
      +            if not resource["urn"].startswith("do:droplet:"):
      +                continue
      +            droplet_id = resource["urn"].replace("do:droplet:", "")
      +            droplet = Droplet.get_object(api_token=self.token, droplet_id=droplet_id)
      +            droplets.append(droplet)
      +
      +        return droplets
      +
      +    def assign_resources(self, resources):
      +        """Assign resources to the project.
      +
      +        :param resources: A list of uniform resource names (URNs) to be added to a project.
      +        :type resources: [str]
      +        """
      +        self.get_data("projects/%s/resources" % self.id, type=POST, params={"resources": resources})
      +
      +    def __str__(self):
      +        return "<Project: %s %s>" % (self.id, self.name)
      +
      +

      Ancestors

      +
        +
      • digitalocean.BaseAPI
      • +
      +

      Static methods

      +
      +
      +def get_object(api_token, project_id) +
      +
      +

      Class method that will return a Project object by ID.

      +

      Args

      +
      +
      api_token : str
      +
      token
      +
      project_id : int
      +
      project id
      +
      +
      + +Expand source code + +
      @classmethod
      +def get_object(cls, api_token, project_id):
      +    """Class method that will return a Project object by ID.
      +
      +    Args:
      +        api_token (str): token
      +        project_id (int): project id
      +    """
      +    project = cls(token=api_token, id=project_id)
      +    project.load()
      +    return project
      +
      +
      +
      +def list(client) +
      +
      +
      +
      + +Expand source code + +
      @classmethod
      +def list(cls, client):
      +
      +    data = client.get_data("projects")
      +
      +    projects = list()
      +    for jsoned in data["projects"]:
      +        project = cls(**jsoned)
      +        project.token = client.token
      +
      +        projects.append(project)
      +
      +    return projects
      +
      +
      +
      +

      Methods

      +
      +
      +def assign_resources(self, resources) +
      +
      +

      Assign resources to the project.

      +

      :param resources: A list of uniform resource names (URNs) to be added to a project. +:type resources: [str]

      +
      + +Expand source code + +
      def assign_resources(self, resources):
      +    """Assign resources to the project.
      +
      +    :param resources: A list of uniform resource names (URNs) to be added to a project.
      +    :type resources: [str]
      +    """
      +    self.get_data("projects/%s/resources" % self.id, type=POST, params={"resources": resources})
      +
      +
      +
      +def create(self, *args, **kwargs) +
      +
      +

      Create the project with object properties.

      +

      Note: Every argument and parameter given to this method will be +assigned to the object.

      +
      + +Expand source code + +
      def create(self, *args, **kwargs):
      +    """
      +    Create the project with object properties.
      +
      +    Note: Every argument and parameter given to this method will be
      +    assigned to the object.
      +    """
      +    for attr in kwargs.keys():
      +        setattr(self, attr, kwargs[attr])
      +
      +    data = {
      +        "name": self.name,
      +        "description": self.description,
      +        "purpose": self.purpose,
      +        "environment": self.environment,
      +    }
      +
      +    data = self.get_data("projects", type=POST, params=data)
      +    self._update_data(data["project"])
      +
      +
      +
      +def delete(self) +
      +
      +

      Delete the project. +To be deleted, a project must not have any resources assigned to it. Any existing resources must first be reassigned or destroyed.

      +
      + +Expand source code + +
      def delete(self):
      +    """
      +    Delete the project.
      +    To be deleted, a project must not have any resources assigned to it. Any existing resources must first be reassigned or destroyed.
      +    """
      +    self.get_data("projects/%s" % self.id, type=DELETE)
      +
      +
      +
      +def list_droplets(self) +
      +
      +

      List all droplets in the project

      +
      + +Expand source code + +
      def list_droplets(self):
      +    """
      +    List all droplets in the project
      +    """
      +    resources = self.list_resources()
      +    droplets = []
      +    for resource in resources:
      +        if not resource["urn"].startswith("do:droplet:"):
      +            continue
      +        droplet_id = resource["urn"].replace("do:droplet:", "")
      +        droplet = Droplet.get_object(api_token=self.token, droplet_id=droplet_id)
      +        droplets.append(droplet)
      +
      +    return droplets
      +
      +
      +
      +def list_resources(self) +
      +
      +

      List all resources in the project

      +
      + +Expand source code + +
      def list_resources(self):
      +    """
      +    List all resources in the project
      +    """
      +    return self.get_data("projects/%s/resources" % self.id)["resources"]
      +
      +
      +
      +def load(self) +
      +
      +

      Fetch data about project - use this instead of get_data()

      +
      + +Expand source code + +
      def load(self):
      +    """
      +    Fetch data about project - use this instead of get_data()
      +    """
      +    projects = self.get_data("projects/%s" % self.id)
      +    project = projects["project"]
      +
      +    for attr in project.keys():
      +        setattr(self, attr, project[attr])
      +
      +    return self
      +
      +
      +
      +def update(self, *args, **kwargs) +
      +
      +

      Update the project with object properties.

      +

      Note: Every argument and parameter given to this method will be +assigned to the object.

      +
      + +Expand source code + +
      def update(self, *args, **kwargs):
      +    """
      +    Update the project with object properties.
      +
      +    Note: Every argument and parameter given to this method will be
      +    assigned to the object.
      +    """
      +    for attr in kwargs.keys():
      +        setattr(self, attr, kwargs[attr])
      +
      +    data = {
      +        "name": self.name,
      +        "description": self.description,
      +        "purpose": self.purpose,
      +        "environment": self.environment,
      +        "is_default": self.is_default,
      +    }
      +
      +    data = self.get_data("projects/%s" % self.id, type=PUT, params=data)
      +    self._update_data(data["project"])
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/docker/docker.html b/docs/api/jumpscale/clients/docker/docker.html index 3fb29f038..0c8c7c65d 100644 --- a/docs/api/jumpscale/clients/docker/docker.html +++ b/docs/api/jumpscale/clients/docker/docker.html @@ -1367,4 +1367,4 @@

      pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/docker/index.html b/docs/api/jumpscale/clients/docker/index.html index c49e3fc2c..517a920ec 100644 --- a/docs/api/jumpscale/clients/docker/index.html +++ b/docs/api/jumpscale/clients/docker/index.html @@ -104,4 +104,4 @@

      Index

      Generated by pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/gedis/gedis.html b/docs/api/jumpscale/clients/gedis/gedis.html new file mode 100644 index 000000000..4ffebed98 --- /dev/null +++ b/docs/api/jumpscale/clients/gedis/gedis.html @@ -0,0 +1,745 @@ + + + + + + +jumpscale.clients.gedis.gedis API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.gedis.gedis

      +
      +
      +
      + +Expand source code + +
      import inspect
      +import json
      +import os
      +import sys
      +from functools import partial
      +
      +from jumpscale.clients.base import Client
      +from jumpscale.core.base import fields
      +from jumpscale.loader import j
      +from jumpscale.servers.gedis.server import GedisErrorTypes, deserialize, serialize
      +from jumpscale.tools.codeloader import load_python_module
      +
      +
      +class ActorResult:
      +    def __init__(self, **kwargs):
      +        self.success = kwargs.get("success", True)
      +        self.result = kwargs.get("result", None)
      +        self.error = kwargs.get("error", None)
      +        self.error_type = kwargs.get("error_type", None)
      +        self.is_async = kwargs.get("is_async", False)
      +        self.task_id = kwargs.get("task_id", None)
      +
      +    def __dir__(self):
      +        return list(self.__dict__.keys())
      +
      +    def __repr__(self):
      +        return str(self.__dict__)
      +
      +
      +class ActorProxy:
      +    def __init__(self, actor_name, actor_info, client):
      +        """ActorProxy to remote actor on the server side
      +
      +        Arguments:
      +            actor_name {str} -- [description]
      +            actor_info {dict} -- actor information dict e.g { method_name: { args: [], 'doc':...} }
      +            gedis_client {GedisClient} -- gedis client reference
      +        """
      +        self.actor_name = actor_name
      +        self.actor_info = actor_info
      +        self.client = client
      +
      +    def __dir__(self):
      +        """Delegate the available functions on the ActorProxy to `actor_info` keys
      +
      +        Returns:
      +            list -- methods available on the ActorProxy
      +        """
      +        return list(self.actor_info["methods"].keys())
      +
      +    def __getattr__(self, method):
      +        """Return a function representing the remote function on the actual actor
      +
      +        Arguments:
      +            attr {str} -- method name
      +
      +        Returns:
      +            function -- function waiting on the arguments
      +        """
      +
      +        def function(*args, **kwargs):
      +            return self.client.execute(self.actor_name, method, *args, **kwargs)
      +
      +        func = partial(function)
      +        func.__doc__ = self.actor_info["methods"][method]["doc"]
      +        return func
      +
      +
      +class ActorsCollection:
      +    def __init__(self, actors):
      +        self._actors = actors
      +
      +    def __dir__(self):
      +        return list(self._actors.keys())
      +
      +    def __getattr__(self, actor_name):
      +        if actor_name in self._actors:
      +            return self._actors[actor_name]
      +
      +
      +class GedisClient(Client):
      +    name = fields.String(default="local")
      +    hostname = fields.String(default="localhost")
      +    port = fields.Integer(default=16000)
      +    raise_on_error = fields.Boolean(default=False)
      +    disable_deserialization = fields.Boolean(default=False)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._redisclient = None
      +        self._loaded_actors = {}
      +        self._loaded_modules = []
      +        self.actors = None
      +        self._load_actors()
      +
      +    @property
      +    def redis_client(self):
      +        if self._redisclient is None:
      +            self._redisclient = j.clients.redis.get(name=f"gedis_{self.name}", hostname=self.hostname, port=self.port)
      +        return self._redisclient
      +
      +    def _load_module(self, path, force_reload=False):
      +        load_python_module(path, force_reload=force_reload)
      +        if path not in self._loaded_modules:
      +            self._loaded_modules.append(path)
      +
      +    def _load_actors(self, force_reload=False):
      +        self._loaded_actors = {}
      +        for actor_name in self.list_actors():
      +            actor_info = self._get_actor_info(actor_name)
      +            self._load_module(actor_info["path"], force_reload=force_reload)
      +            self._loaded_actors[actor_name] = ActorProxy(actor_name, actor_info, self)
      +
      +        self.actors = ActorsCollection(self._loaded_actors)
      +
      +    def _get_actor_info(self, actor_name):
      +        return self.execute(actor_name, "info", die=True).result
      +
      +    def list_actors(self) -> list:
      +        """List actors
      +
      +        Returns:
      +            list -- List of loaded actors
      +        """
      +        return self.execute("core", "list_actors", die=True).result
      +
      +    def reload(self):
      +        """Reload actors
      +        """
      +        self._load_actors(force_reload=True)
      +
      +    def execute(self, actor_name: str, actor_method: str, *args, die: bool = False, **kwargs) -> ActorResult:
      +        """Execute actor's method
      +
      +        Arguments:
      +            actor_name {str} -- actor name
      +            actor_method {str} -- actor method
      +
      +        Keyword Arguments:
      +            die {bool} --  flag to raise an error when request fails (default: {False})
      +
      +        Raises:
      +            RemoteException: Raises if the request failed and raise_on_error flag is set
      +
      +        Returns:
      +            ActorResult -- request result
      +        """
      +        payload = json.dumps((args, kwargs), default=serialize)
      +        response = self.redis_client.execute_command(actor_name, actor_method, payload)
      +
      +        deserializer = deserialize if not self.disable_deserialization else None
      +        response = json.loads(response, object_hook=deserializer)
      +
      +        if not response["success"]:
      +            if die or self.raise_on_error:
      +                raise RemoteException(response["error"])
      +
      +            response["error_type"] = GedisErrorTypes(response["error_type"])
      +
      +        return ActorResult(**response)
      +
      +
      +class RemoteException(Exception):
      +    pass
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class ActorProxy +(actor_name, actor_info, client) +
      +
      +

      ActorProxy to remote actor on the server side

      +

      Arguments

      +

      actor_name {str} – [description] +actor_info {dict} – actor information dict e.g { method_name: { args: [], 'doc':…} } +gedis_client {GedisClient} – gedis client reference

      +
      + +Expand source code + +
      class ActorProxy:
      +    def __init__(self, actor_name, actor_info, client):
      +        """ActorProxy to remote actor on the server side
      +
      +        Arguments:
      +            actor_name {str} -- [description]
      +            actor_info {dict} -- actor information dict e.g { method_name: { args: [], 'doc':...} }
      +            gedis_client {GedisClient} -- gedis client reference
      +        """
      +        self.actor_name = actor_name
      +        self.actor_info = actor_info
      +        self.client = client
      +
      +    def __dir__(self):
      +        """Delegate the available functions on the ActorProxy to `actor_info` keys
      +
      +        Returns:
      +            list -- methods available on the ActorProxy
      +        """
      +        return list(self.actor_info["methods"].keys())
      +
      +    def __getattr__(self, method):
      +        """Return a function representing the remote function on the actual actor
      +
      +        Arguments:
      +            attr {str} -- method name
      +
      +        Returns:
      +            function -- function waiting on the arguments
      +        """
      +
      +        def function(*args, **kwargs):
      +            return self.client.execute(self.actor_name, method, *args, **kwargs)
      +
      +        func = partial(function)
      +        func.__doc__ = self.actor_info["methods"][method]["doc"]
      +        return func
      +
      +
      +
      +class ActorResult +(**kwargs) +
      +
      +
      +
      + +Expand source code + +
      class ActorResult:
      +    def __init__(self, **kwargs):
      +        self.success = kwargs.get("success", True)
      +        self.result = kwargs.get("result", None)
      +        self.error = kwargs.get("error", None)
      +        self.error_type = kwargs.get("error_type", None)
      +        self.is_async = kwargs.get("is_async", False)
      +        self.task_id = kwargs.get("task_id", None)
      +
      +    def __dir__(self):
      +        return list(self.__dict__.keys())
      +
      +    def __repr__(self):
      +        return str(self.__dict__)
      +
      +
      +
      +class ActorsCollection +(actors) +
      +
      +
      +
      + +Expand source code + +
      class ActorsCollection:
      +    def __init__(self, actors):
      +        self._actors = actors
      +
      +    def __dir__(self):
      +        return list(self._actors.keys())
      +
      +    def __getattr__(self, actor_name):
      +        if actor_name in self._actors:
      +            return self._actors[actor_name]
      +
      +
      +
      +class GedisClient +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class GedisClient(Client):
      +    name = fields.String(default="local")
      +    hostname = fields.String(default="localhost")
      +    port = fields.Integer(default=16000)
      +    raise_on_error = fields.Boolean(default=False)
      +    disable_deserialization = fields.Boolean(default=False)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._redisclient = None
      +        self._loaded_actors = {}
      +        self._loaded_modules = []
      +        self.actors = None
      +        self._load_actors()
      +
      +    @property
      +    def redis_client(self):
      +        if self._redisclient is None:
      +            self._redisclient = j.clients.redis.get(name=f"gedis_{self.name}", hostname=self.hostname, port=self.port)
      +        return self._redisclient
      +
      +    def _load_module(self, path, force_reload=False):
      +        load_python_module(path, force_reload=force_reload)
      +        if path not in self._loaded_modules:
      +            self._loaded_modules.append(path)
      +
      +    def _load_actors(self, force_reload=False):
      +        self._loaded_actors = {}
      +        for actor_name in self.list_actors():
      +            actor_info = self._get_actor_info(actor_name)
      +            self._load_module(actor_info["path"], force_reload=force_reload)
      +            self._loaded_actors[actor_name] = ActorProxy(actor_name, actor_info, self)
      +
      +        self.actors = ActorsCollection(self._loaded_actors)
      +
      +    def _get_actor_info(self, actor_name):
      +        return self.execute(actor_name, "info", die=True).result
      +
      +    def list_actors(self) -> list:
      +        """List actors
      +
      +        Returns:
      +            list -- List of loaded actors
      +        """
      +        return self.execute("core", "list_actors", die=True).result
      +
      +    def reload(self):
      +        """Reload actors
      +        """
      +        self._load_actors(force_reload=True)
      +
      +    def execute(self, actor_name: str, actor_method: str, *args, die: bool = False, **kwargs) -> ActorResult:
      +        """Execute actor's method
      +
      +        Arguments:
      +            actor_name {str} -- actor name
      +            actor_method {str} -- actor method
      +
      +        Keyword Arguments:
      +            die {bool} --  flag to raise an error when request fails (default: {False})
      +
      +        Raises:
      +            RemoteException: Raises if the request failed and raise_on_error flag is set
      +
      +        Returns:
      +            ActorResult -- request result
      +        """
      +        payload = json.dumps((args, kwargs), default=serialize)
      +        response = self.redis_client.execute_command(actor_name, actor_method, payload)
      +
      +        deserializer = deserialize if not self.disable_deserialization else None
      +        response = json.loads(response, object_hook=deserializer)
      +
      +        if not response["success"]:
      +            if die or self.raise_on_error:
      +                raise RemoteException(response["error"])
      +
      +            response["error_type"] = GedisErrorTypes(response["error_type"])
      +
      +        return ActorResult(**response)
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var disable_deserialization
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var hostname
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var name
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var port
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var raise_on_error
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var redis_client
      +
      +
      +
      + +Expand source code + +
      @property
      +def redis_client(self):
      +    if self._redisclient is None:
      +        self._redisclient = j.clients.redis.get(name=f"gedis_{self.name}", hostname=self.hostname, port=self.port)
      +    return self._redisclient
      +
      +
      +
      +

      Methods

      +
      +
      +def execute(self, actor_name: str, actor_method: str, *args, die: bool = False, **kwargs) ‑> ActorResult +
      +
      +

      Execute actor's method

      +

      Arguments

      +

      actor_name {str} – actor name +actor_method {str} – actor method

      +

      Keyword Arguments: +die {bool} – +flag to raise an error when request fails (default: {False})

      +

      Raises

      +
      +
      RemoteException
      +
      Raises if the request failed and raise_on_error flag is set
      +
      +

      Returns

      +

      ActorResult – request result

      +
      + +Expand source code + +
      def execute(self, actor_name: str, actor_method: str, *args, die: bool = False, **kwargs) -> ActorResult:
      +    """Execute actor's method
      +
      +    Arguments:
      +        actor_name {str} -- actor name
      +        actor_method {str} -- actor method
      +
      +    Keyword Arguments:
      +        die {bool} --  flag to raise an error when request fails (default: {False})
      +
      +    Raises:
      +        RemoteException: Raises if the request failed and raise_on_error flag is set
      +
      +    Returns:
      +        ActorResult -- request result
      +    """
      +    payload = json.dumps((args, kwargs), default=serialize)
      +    response = self.redis_client.execute_command(actor_name, actor_method, payload)
      +
      +    deserializer = deserialize if not self.disable_deserialization else None
      +    response = json.loads(response, object_hook=deserializer)
      +
      +    if not response["success"]:
      +        if die or self.raise_on_error:
      +            raise RemoteException(response["error"])
      +
      +        response["error_type"] = GedisErrorTypes(response["error_type"])
      +
      +    return ActorResult(**response)
      +
      +
      +
      +def list_actors(self) ‑> list +
      +
      +

      List actors

      +

      Returns

      +

      list – List of loaded actors

      +
      + +Expand source code + +
      def list_actors(self) -> list:
      +    """List actors
      +
      +    Returns:
      +        list -- List of loaded actors
      +    """
      +    return self.execute("core", "list_actors", die=True).result
      +
      +
      +
      +def reload(self) +
      +
      +

      Reload actors

      +
      + +Expand source code + +
      def reload(self):
      +    """Reload actors
      +    """
      +    self._load_actors(force_reload=True)
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class RemoteException +(*args, **kwargs) +
      +
      +

      Common base class for all non-exit exceptions.

      +
      + +Expand source code + +
      class RemoteException(Exception):
      +    pass
      +
      +

      Ancestors

      +
        +
      • builtins.Exception
      • +
      • builtins.BaseException
      • +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/gedis/index.html b/docs/api/jumpscale/clients/gedis/index.html new file mode 100644 index 000000000..a2514cd47 --- /dev/null +++ b/docs/api/jumpscale/clients/gedis/index.html @@ -0,0 +1,242 @@ + + + + + + +jumpscale.clients.gedis API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.gedis

      +
      +
      +

      This module gives you all the facilities to communicate with gedis server

      +

      Connecting to a gedis server

      +
      JS-NG> gedis = j.clients.gedis.get("local")
      +JS-NG> gedis.list_actors()
      +['system']
      +
      +

      Registering actor

      +
      JS-NG> gedis.actors.system.register_actor("greeter", "/home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py")
      +1
      +
      +

      Listing actors

      +
      JS-NG> gedis.list_actors()
      +['system', 'greeter']
      +
      +

      Documentation of an actor

      +
      JS-NG> gedis.ppdoc("greeter")
      +{
      +  "add2": {
      +    "args": [
      +      "a",
      +      "b"
      +    ],
      +    "doc": "Add two args
      +
      +        "
      +  },
      +  "hi": {
      +    "args": [],
      +    "doc": "returns hello world
      +        "
      +  },
      +  "info": {
      +    "args": [
      +      "result",
      +      "members",
      +      "name",
      +      "attr"
      +    ],
      +    "doc": ""
      +  },
      +  "ping": {
      +    "args": [],
      +    "doc": "
      +
      +        "
      +  }
      +}
      +
      +

      Invoking an actor method

      +
      JS-NG> gedis.execute("greeter", "hi")
      +b'hello world'
      +
      +JS-NG> gedis.execute("greeter", "ping")
      +b'pong no?'
      +
      +JS-NG> gedis.execute("greeter", "add2", "first", "second")
      +b'firstsecond'
      +
      +

      Invoking actor method with attribute access

      +
      JS-NG> gedis.actors.greeter.hi()
      +b'hello world'
      +
      +JS-NG> gedis.actors.greeter.add2("a", "b")
      +b'ab'
      +
      +
      + +Expand source code + +
      """This module gives you all the facilities to communicate with gedis server
      +
      +Connecting to a gedis server
      +```
      +JS-NG> gedis = j.clients.gedis.get("local")
      +JS-NG> gedis.list_actors()
      +['system']
      +```
      +
      +Registering actor
      +```
      +JS-NG> gedis.actors.system.register_actor("greeter", "/home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py")
      +1
      +```
      +
      +Listing actors
      +```
      +JS-NG> gedis.list_actors()
      +['system', 'greeter']
      +```
      +
      +Documentation of an actor
      +
      +```
      +JS-NG> gedis.ppdoc("greeter")
      +{
      +  "add2": {
      +    "args": [
      +      "a",
      +      "b"
      +    ],
      +    "doc": "Add two args\n        \n        "
      +  },
      +  "hi": {
      +    "args": [],
      +    "doc": "returns hello world\n        "
      +  },
      +  "info": {
      +    "args": [
      +      "result",
      +      "members",
      +      "name",
      +      "attr"
      +    ],
      +    "doc": ""
      +  },
      +  "ping": {
      +    "args": [],
      +    "doc": "\n        \n        "
      +  }
      +}
      +```
      +Invoking an actor method
      +```
      +JS-NG> gedis.execute("greeter", "hi")
      +b'hello world'
      +
      +JS-NG> gedis.execute("greeter", "ping")
      +b'pong no?'
      +
      +JS-NG> gedis.execute("greeter", "add2", "first", "second")
      +b'firstsecond'
      +```
      +Invoking actor method with attribute access
      +```
      +JS-NG> gedis.actors.greeter.hi()
      +b'hello world'
      +
      +JS-NG> gedis.actors.greeter.add2("a", "b")
      +b'ab'
      +```
      +"""
      +
      +
      +def export_module_as():
      +
      +    from jumpscale.core.base import StoredFactory
      +
      +    from .gedis import GedisClient
      +
      +    return StoredFactory(GedisClient)
      +
      +
      +
      +

      Sub-modules

      +
      +
      jumpscale.clients.gedis.gedis
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +
      +    from jumpscale.core.base import StoredFactory
      +
      +    from .gedis import GedisClient
      +
      +    return StoredFactory(GedisClient)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/git/git.html b/docs/api/jumpscale/clients/git/git.html index a84c1bed1..a770fabf8 100644 --- a/docs/api/jumpscale/clients/git/git.html +++ b/docs/api/jumpscale/clients/git/git.html @@ -445,4 +445,4 @@

      pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/git/index.html b/docs/api/jumpscale/clients/git/index.html index 1a9fbd71a..4a7accde8 100644 --- a/docs/api/jumpscale/clients/git/index.html +++ b/docs/api/jumpscale/clients/git/index.html @@ -100,4 +100,4 @@

      Index

      Generated by pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/github/base.html b/docs/api/jumpscale/clients/github/base.html new file mode 100644 index 000000000..0665d09f0 --- /dev/null +++ b/docs/api/jumpscale/clients/github/base.html @@ -0,0 +1,240 @@ + + + + + + +jumpscale.clients.github.base API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.github.base

      +
      +
      +
      + +Expand source code + +
      from jumpscale.clients.base import Client
      +from jumpscale.core.base import Base, fields
      +from jumpscale.loader import j
      +
      +replacelabels = {
      +    "bug": "type_bug",
      +    "duplicate": "process_duplicate",
      +    "enhancement": "type_feature",
      +    "help wanted": "state_question",
      +    "invalid": "state_question",
      +    "question": "state_question",
      +    "wontfix": "process_wontfix",
      +    "completed": "state_verification",
      +    "in progress": "state_inprogress",
      +    "ready": "state_verification",
      +    "story": "type_story",
      +    "urgent": "priority_urgent",
      +    "type_bug": "type_unknown",
      +    "type_story": "type_unknown",
      +}
      +
      +
      +class base(Base):
      +    def __init__(self):
      +        super().__init__()
      +
      +    @property
      +    def body_without_tags(self):
      +        # remove the tag lines from the body
      +        out = ""
      +        if self.body is None:
      +            return ""
      +        for line in self.body.split("\n"):
      +            if line.startswith("##") and not line.startswith("###"):
      +                continue
      +            out += "%s\n" % line
      +
      +        out = out.rstrip() + "\n"
      +        return out
      +
      +    # @tags.setter
      +    # def tags(self, ddict):
      +    #     if isinstance(ddict,dict) is False:
      +    #         raise Exception("Tags need to be dict as input for setter, now:%s" % ddict)
      +
      +    #     keys = sorted(ddict.keys())
      +
      +    #     out = self.body_without_tags + "\n"
      +    #     for key, val in ddict.items():
      +    #         out += ".. %s:%s\n" % (key, val)
      +
      +    #     self.body = out
      +    #     return self.tags
      +
      +    def __str__(self):
      +        return str(self._ddict)
      +
      +    __repr__ = __str__
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class base +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class base(Base):
      +    def __init__(self):
      +        super().__init__()
      +
      +    @property
      +    def body_without_tags(self):
      +        # remove the tag lines from the body
      +        out = ""
      +        if self.body is None:
      +            return ""
      +        for line in self.body.split("\n"):
      +            if line.startswith("##") and not line.startswith("###"):
      +                continue
      +            out += "%s\n" % line
      +
      +        out = out.rstrip() + "\n"
      +        return out
      +
      +    # @tags.setter
      +    # def tags(self, ddict):
      +    #     if isinstance(ddict,dict) is False:
      +    #         raise Exception("Tags need to be dict as input for setter, now:%s" % ddict)
      +
      +    #     keys = sorted(ddict.keys())
      +
      +    #     out = self.body_without_tags + "\n"
      +    #     for key, val in ddict.items():
      +    #         out += ".. %s:%s\n" % (key, val)
      +
      +    #     self.body = out
      +    #     return self.tags
      +
      +    def __str__(self):
      +        return str(self._ddict)
      +
      +    __repr__ = __str__
      +
      +

      Ancestors

      +
        +
      • Base
      • +
      • types.SimpleNamespace
      • +
      +

      Subclasses

      + +

      Instance variables

      +
      +
      var body_without_tags
      +
      +
      +
      + +Expand source code + +
      @property
      +def body_without_tags(self):
      +    # remove the tag lines from the body
      +    out = ""
      +    if self.body is None:
      +        return ""
      +    for line in self.body.split("\n"):
      +        if line.startswith("##") and not line.startswith("###"):
      +            continue
      +        out += "%s\n" % line
      +
      +    out = out.rstrip() + "\n"
      +    return out
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/github/github.html b/docs/api/jumpscale/clients/github/github.html new file mode 100644 index 000000000..fe3f8ee1d --- /dev/null +++ b/docs/api/jumpscale/clients/github/github.html @@ -0,0 +1,522 @@ + + + + + + +jumpscale.clients.github.github API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.github.github

      +
      +
      +
      + +Expand source code + +
      from gevent import sleep
      +from jumpscale.clients.base import Client
      +from jumpscale.core.base import Base, fields
      +from jumpscale.loader import j
      +
      +from github import Github, GithubObject
      +
      +from .repo import GithubRepo
      +from .helper import retry
      +
      +NotSet = GithubObject.NotSet
      +
      +
      +class GithubClient(Client):
      +    username = fields.String()
      +    password = fields.String()
      +    accesstoken = fields.String()
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self.__client = None
      +
      +    @property
      +    def github_client(self):
      +        if not self.__client:
      +            if self.accesstoken:
      +                self.__client = Github(self.accesstoken)
      +            else:
      +                self.__client = Github(login_or_token=self.username, password=self.password)
      +        return self.__client
      +
      +    @retry
      +    def get_repo(self, repo_full_name):
      +        return GithubRepo(self.github_client, repo_full_name)
      +
      +    @retry
      +    def get_repos(self):
      +        l = []
      +        for r in self.github_client.get_user().get_repos():
      +            l.append(GithubRepo(self.github_client, r.full_name))
      +        return l
      +
      +    @retry
      +    def get_orgs(self):
      +        l = []
      +        for o in self.github_client.get_user().get_orgs():
      +            l.append(o.login)
      +        return l
      +
      +    @retry
      +    def get_userdata(self):
      +        u = self.github_client.get_user()
      +        el = []
      +        for e in u.get_emails():
      +            el.append(e)
      +        return {"name": u.name, "emails": el, "id": u.id, "avatar_url": u.avatar_url}
      +
      +    @retry
      +    def create_repo(
      +        self,
      +        name,
      +        description=NotSet,
      +        homepage=NotSet,
      +        private=NotSet,
      +        has_issues=NotSet,
      +        has_wiki=NotSet,
      +        has_downloads=NotSet,
      +        auto_init=NotSet,
      +        gitignore_template=NotSet,
      +    ):
      +
      +        return self.github_client.get_user().create_repo(
      +            name,
      +            description=description,
      +            homepage=homepage,
      +            private=private,
      +            has_issues=has_issues,
      +            has_wiki=has_wiki,
      +            has_downloads=has_downloads,
      +            auto_init=auto_init,
      +            gitignore_template=gitignore_template,
      +        )
      +
      +    @retry
      +    def delete_repo(self, repo_name):
      +        return self.github_client.get_user().get_repo(repo_name).delete()
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class GithubClient +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class GithubClient(Client):
      +    username = fields.String()
      +    password = fields.String()
      +    accesstoken = fields.String()
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self.__client = None
      +
      +    @property
      +    def github_client(self):
      +        if not self.__client:
      +            if self.accesstoken:
      +                self.__client = Github(self.accesstoken)
      +            else:
      +                self.__client = Github(login_or_token=self.username, password=self.password)
      +        return self.__client
      +
      +    @retry
      +    def get_repo(self, repo_full_name):
      +        return GithubRepo(self.github_client, repo_full_name)
      +
      +    @retry
      +    def get_repos(self):
      +        l = []
      +        for r in self.github_client.get_user().get_repos():
      +            l.append(GithubRepo(self.github_client, r.full_name))
      +        return l
      +
      +    @retry
      +    def get_orgs(self):
      +        l = []
      +        for o in self.github_client.get_user().get_orgs():
      +            l.append(o.login)
      +        return l
      +
      +    @retry
      +    def get_userdata(self):
      +        u = self.github_client.get_user()
      +        el = []
      +        for e in u.get_emails():
      +            el.append(e)
      +        return {"name": u.name, "emails": el, "id": u.id, "avatar_url": u.avatar_url}
      +
      +    @retry
      +    def create_repo(
      +        self,
      +        name,
      +        description=NotSet,
      +        homepage=NotSet,
      +        private=NotSet,
      +        has_issues=NotSet,
      +        has_wiki=NotSet,
      +        has_downloads=NotSet,
      +        auto_init=NotSet,
      +        gitignore_template=NotSet,
      +    ):
      +
      +        return self.github_client.get_user().create_repo(
      +            name,
      +            description=description,
      +            homepage=homepage,
      +            private=private,
      +            has_issues=has_issues,
      +            has_wiki=has_wiki,
      +            has_downloads=has_downloads,
      +            auto_init=auto_init,
      +            gitignore_template=gitignore_template,
      +        )
      +
      +    @retry
      +    def delete_repo(self, repo_name):
      +        return self.github_client.get_user().get_repo(repo_name).delete()
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var accesstoken
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var github_client
      +
      +
      +
      + +Expand source code + +
      @property
      +def github_client(self):
      +    if not self.__client:
      +        if self.accesstoken:
      +            self.__client = Github(self.accesstoken)
      +        else:
      +            self.__client = Github(login_or_token=self.username, password=self.password)
      +    return self.__client
      +
      +
      +
      var password
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var username
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def create_repo(self, *args, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def wrapper(self, *args, **kwargs):
      +    for _ in range(6):
      +        try:
      +            result = function(self, *args, **kwargs)
      +            break
      +        except Exception as e:
      +            j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +            sleep(1)
      +    else:
      +        raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +    return result
      +
      +
      +
      +def delete_repo(self, *args, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def wrapper(self, *args, **kwargs):
      +    for _ in range(6):
      +        try:
      +            result = function(self, *args, **kwargs)
      +            break
      +        except Exception as e:
      +            j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +            sleep(1)
      +    else:
      +        raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +    return result
      +
      +
      +
      +def get_orgs(self, *args, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def wrapper(self, *args, **kwargs):
      +    for _ in range(6):
      +        try:
      +            result = function(self, *args, **kwargs)
      +            break
      +        except Exception as e:
      +            j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +            sleep(1)
      +    else:
      +        raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +    return result
      +
      +
      +
      +def get_repo(self, *args, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def wrapper(self, *args, **kwargs):
      +    for _ in range(6):
      +        try:
      +            result = function(self, *args, **kwargs)
      +            break
      +        except Exception as e:
      +            j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +            sleep(1)
      +    else:
      +        raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +    return result
      +
      +
      +
      +def get_repos(self, *args, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def wrapper(self, *args, **kwargs):
      +    for _ in range(6):
      +        try:
      +            result = function(self, *args, **kwargs)
      +            break
      +        except Exception as e:
      +            j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +            sleep(1)
      +    else:
      +        raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +    return result
      +
      +
      +
      +def get_userdata(self, *args, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def wrapper(self, *args, **kwargs):
      +    for _ in range(6):
      +        try:
      +            result = function(self, *args, **kwargs)
      +            break
      +        except Exception as e:
      +            j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +            sleep(1)
      +    else:
      +        raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +    return result
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/github/helper.html b/docs/api/jumpscale/clients/github/helper.html new file mode 100644 index 000000000..489d964e8 --- /dev/null +++ b/docs/api/jumpscale/clients/github/helper.html @@ -0,0 +1,109 @@ + + + + + + +jumpscale.clients.github.helper API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.github.helper

      +
      +
      +
      + +Expand source code + +
      from gevent import sleep
      +from jumpscale.loader import j
      +
      +
      +def retry(function):
      +    def wrapper(self, *args, **kwargs):
      +        for _ in range(6):
      +            try:
      +                result = function(self, *args, **kwargs)
      +                break
      +            except Exception as e:
      +                j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +                sleep(1)
      +        else:
      +            raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +        return result
      +
      +    return wrapper
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def retry(function) +
      +
      +
      +
      + +Expand source code + +
      def retry(function):
      +    def wrapper(self, *args, **kwargs):
      +        for _ in range(6):
      +            try:
      +                result = function(self, *args, **kwargs)
      +                break
      +            except Exception as e:
      +                j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +                sleep(1)
      +        else:
      +            raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +        return result
      +
      +    return wrapper
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/github/index.html b/docs/api/jumpscale/clients/github/index.html new file mode 100644 index 000000000..8a59fc351 --- /dev/null +++ b/docs/api/jumpscale/clients/github/index.html @@ -0,0 +1,124 @@ + + + + + + +jumpscale.clients.github API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.github

      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import StoredFactory
      +    from .github import GithubClient
      +
      +    return StoredFactory(GithubClient)
      +
      +
      +
      +

      Sub-modules

      +
      +
      jumpscale.clients.github.base
      +
      +
      +
      +
      jumpscale.clients.github.github
      +
      +
      +
      +
      jumpscale.clients.github.helper
      +
      +
      +
      +
      jumpscale.clients.github.issue
      +
      +
      +
      +
      jumpscale.clients.github.milestone
      +
      +
      +
      +
      jumpscale.clients.github.repo
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import StoredFactory
      +    from .github import GithubClient
      +
      +    return StoredFactory(GithubClient)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/github/issue.html b/docs/api/jumpscale/clients/github/issue.html new file mode 100644 index 000000000..4a476d4bd --- /dev/null +++ b/docs/api/jumpscale/clients/github/issue.html @@ -0,0 +1,1138 @@ + + + + + + +jumpscale.clients.github.issue API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.github.issue

      +
      +
      +
      + +Expand source code + +
      from jumpscale.loader import j
      +from .base import base
      +from .base import replacelabels
      +from .milestone import RepoMilestone
      +
      +
      +class Issue(base):
      +    def __init__(self, repo, ddict={}, githubObj=None):
      +        base.__init__(self)
      +        self.repo = repo
      +        self._ddict = ddict
      +        self._githubObj = githubObj
      +        self._comments = ddict.get("comments", None)
      +        if githubObj is not None:
      +            self.load()
      +
      +        self._lock = threading.RLock()
      +        # self.todo
      +
      +    @property
      +    def api(self):
      +        if self._githubObj is None:
      +            self._githubObj = self.repo.api.get_issue(self.number)
      +        return self._githubObj
      +
      +    @property
      +    def ddict(self):
      +        if self._ddict == {}:
      +            # no dict yet, fetch from github
      +            self.load()
      +        # we lazy load the comments. so it's only loaded when accesses
      +        self._ddict["comments"] = self.comments
      +        return self._ddict
      +
      +    @property
      +    def comments(self):
      +        if self._comments is not None:
      +            return self._comments
      +
      +        with self._lock:
      +            if self._comments is None:
      +                self._log_debug("Loading comments for issue: %s" % self.number)
      +                self._comments = []
      +                for comment in self.api.get_comments():
      +                    obj = {}
      +                    user = self.repo.client.getUserLogin(githubObj=comment.user)
      +                    obj["user"] = user
      +                    obj["url"] = comment.url
      +                    obj["id"] = comment.id
      +                    obj["body"] = comment.body
      +                    obj["user_id"] = comment.user.id
      +                    # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
      +                    self._comments.append(obj)
      +        return self._comments
      +
      +    def reload_comments(self):
      +        with self._lock:
      +            self._comments = []
      +            for comment in self.api.get_comments():
      +                obj = {}
      +                user = self.repo.client.getUserLogin(githubObj=comment.user)
      +                obj["user"] = user
      +                obj["url"] = comment.url
      +                obj["id"] = comment.id
      +                obj["body"] = comment.body
      +                # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
      +                self._comments.append(obj)
      +        return self._comments
      +
      +    @property
      +    def guid(self):
      +        return self.repo.fullname + "_" + str(self._ddict["number"])
      +
      +    @property
      +    def number(self):
      +        return int(self._ddict["number"])
      +
      +    @property
      +    def title(self):
      +        return self._ddict["title"]
      +
      +    @property
      +    def body(self):
      +        return self._ddict["body"]
      +
      +    @body.setter
      +    def body(self, val):
      +        self._ddict["body"] = val
      +        try:
      +            self.api.edit(body=self._ddict["body"])
      +        except Exception as e:
      +            self._log_error("Failed to update the issue body: %s" % e)
      +
      +    @property
      +    def time(self):
      +        return self._ddict["time"]
      +
      +    @property
      +    def url(self):
      +        return self._ddict["url"]
      +
      +    @property
      +    def assignee(self):
      +        return self._ddict["assignee"]
      +
      +    @property
      +    def labels(self):
      +        # we return a copy so changing the list doesn't actually change the
      +        # ddict value
      +        return self._ddict["labels"][:]
      +
      +    @property
      +    def id(self):
      +        return self._ddict["id"]
      +
      +    @labels.setter
      +    def labels(self, val):
      +        # check if all are already in labels, if yes nothing to do
      +        if len(val) == len(self._ddict["labels"]):
      +            self._ddict["labels"].sort()
      +            val.sort()
      +            if val == self._ddict["labels"]:
      +                return
      +        self._ddict["labels"] = val
      +        toset = [self.repo.getLabel(item) for item in self._ddict["labels"]]
      +        self.api.set_labels(*toset)
      +
      +    @property
      +    def milestone(self):
      +        return self._ddict["milestone"]
      +
      +    @property
      +    def state(self):
      +        states = []
      +        if not self.is_open:
      +            return "closed"
      +
      +        for label in self.labels:
      +            if label.startswith("state"):
      +                states.append(label)
      +        if len(states) == 1:
      +            return states[0][len("state") :].strip("_")
      +        elif len(states) > 1:
      +            self.state = "question"
      +        else:
      +            return ""
      +
      +    @state.setter
      +    def state(self, val):
      +        return self._setLabels(val, "state")
      +
      +    @property
      +    def is_open(self):
      +        return self._ddict["open"]
      +
      +    @property
      +    def type(self):
      +        items = []
      +        for label in self.labels:
      +            if label.startswith("type"):
      +                items.append(label)
      +        if len(items) == 1:
      +            return items[0].partition("_")[-1]
      +
      +        return ""
      +
      +    @type.setter
      +    def type(self, val):
      +        return self._setLabels(val, "type")
      +
      +    @property
      +    def priority(self):
      +        items = []
      +        for label in self.labels:
      +            if label.startswith("priority"):
      +                items.append(label)
      +        if len(items) == 1:
      +            return items[0].partition("_")[-1]
      +        else:
      +            self.priority = "normal"
      +            return self.priority
      +
      +    @priority.setter
      +    def priority(self, val):
      +        return self._setLabels(val, "priority")
      +
      +    @property
      +    def process(self):
      +        items = []
      +        for label in self.labels:
      +            if label.startswith("process"):
      +                items.append(label)
      +        if len(items) == 1:
      +            return items[0][len("process") :].strip("_")
      +        else:
      +            return ""
      +
      +    @process.setter
      +    def process(self, val):
      +        return self._setLabels(val, "process")
      +
      +    def _setLabels(self, val, category):
      +        if val is None or val == "":
      +            return
      +
      +        if val.startswith(category):
      +            _, _, val = val.partition("_")
      +
      +        val = val.strip("_")
      +        val = val.lower()
      +
      +        val = "%s_%s" % (category, val)
      +
      +        if val not in self.repo.labelnames:
      +            self.repo.labelnames.sort()
      +            llist = ",".join(self.repo.labelnames)
      +            raise Exception(
      +                "Label needs to be in list:%s (is understood labels in this repo on github), now is: '%s'"
      +                % (llist, val)
      +            )
      +
      +        # make sure there is only 1
      +        labels2set = self.labels
      +        items = []
      +        for label in self.labels:
      +            if label.startswith(category):
      +                items.append(label)
      +        if len(items) == 1 and val in items:
      +            return
      +        for item in items:
      +            labels2set.pop(labels2set.index(item))
      +        if val is not None or val != "":
      +            labels2set.append(val)
      +        self.labels = labels2set
      +
      +    def load(self):
      +
      +        self._ddict = {}
      +
      +        # check labels
      +        labels = [item.name for item in self.api.labels]  # are the names
      +        newlabels = []
      +        for label in labels:
      +            if label not in self.repo.labelnames:
      +                if label in replacelabels:
      +                    if replacelabels[label] not in newlabels:
      +                        newlabels.append(replacelabels[label])
      +            else:
      +                if label not in newlabels:
      +                    newlabels.append(label)
      +
      +        if labels != newlabels:
      +            self._log_info("change label:%s for %s" % (labels, self.api.title))
      +            labels2set = [self.repo.getLabel(item) for item in newlabels]
      +            self.api.set_labels(*labels2set)
      +            labels = newlabels
      +
      +        self._ddict["labels"] = labels
      +        self._ddict["id"] = self.api.id
      +        self._ddict["url"] = self.api.html_url
      +        self._ddict["number"] = self.api.number
      +        self._ddict["open"] = self.api.state == "open"
      +
      +        self._ddict["assignee"] = self.repo.client.getUserLogin(githubObj=self.api.assignee)
      +        self._ddict["state"] = self.api.state
      +        self._ddict["title"] = self.api.title
      +
      +        self._ddict["body"] = self.api.body
      +
      +        # self._ddict["time"] = j.data.time.any2HRDateTime([self.api.last_modified, self.api.created_at])
      +
      +        self._log_debug("LOAD:%s %s" % (self.repo.fullname, self._ddict["title"]))
      +
      +        if self.api.milestone is None:
      +            self._ddict["milestone"] = ""
      +        else:
      +            ms = RepoMilestone(repo=self.repo, githubObj=self.api.milestone)
      +            self._ddict["milestone"] = "%s:%s" % (ms.number, ms.title)
      +
      +    @property
      +    def todo(self):
      +        if "_todo" not in self.__dict__:
      +            todo = []
      +            if self.body is not None:
      +                for line in self.body.split("\n"):
      +                    if line.startswith("!! "):
      +                        todo.append(line.strip().strip("!! "))
      +            for comment in self.comments:
      +                for line in comment["body"].split("\n"):
      +                    if line.startswith("!! "):
      +                        todo.append(line.strip().strip("!! "))
      +            self._todo = todo
      +        return self._todo
      +
      +    @property
      +    def istask(self):
      +        if self.type == "task" or self.title.lower().endswith("task"):
      +            return True
      +        return False
      +
      +    def __str__(self):
      +        return "issue:%s" % self.title
      +
      +    __repr__ = __str__
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class Issue +(repo, ddict={}, githubObj=None) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class Issue(base):
      +    def __init__(self, repo, ddict={}, githubObj=None):
      +        base.__init__(self)
      +        self.repo = repo
      +        self._ddict = ddict
      +        self._githubObj = githubObj
      +        self._comments = ddict.get("comments", None)
      +        if githubObj is not None:
      +            self.load()
      +
      +        self._lock = threading.RLock()
      +        # self.todo
      +
      +    @property
      +    def api(self):
      +        if self._githubObj is None:
      +            self._githubObj = self.repo.api.get_issue(self.number)
      +        return self._githubObj
      +
      +    @property
      +    def ddict(self):
      +        if self._ddict == {}:
      +            # no dict yet, fetch from github
      +            self.load()
      +        # we lazy load the comments. so it's only loaded when accesses
      +        self._ddict["comments"] = self.comments
      +        return self._ddict
      +
      +    @property
      +    def comments(self):
      +        if self._comments is not None:
      +            return self._comments
      +
      +        with self._lock:
      +            if self._comments is None:
      +                self._log_debug("Loading comments for issue: %s" % self.number)
      +                self._comments = []
      +                for comment in self.api.get_comments():
      +                    obj = {}
      +                    user = self.repo.client.getUserLogin(githubObj=comment.user)
      +                    obj["user"] = user
      +                    obj["url"] = comment.url
      +                    obj["id"] = comment.id
      +                    obj["body"] = comment.body
      +                    obj["user_id"] = comment.user.id
      +                    # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
      +                    self._comments.append(obj)
      +        return self._comments
      +
      +    def reload_comments(self):
      +        with self._lock:
      +            self._comments = []
      +            for comment in self.api.get_comments():
      +                obj = {}
      +                user = self.repo.client.getUserLogin(githubObj=comment.user)
      +                obj["user"] = user
      +                obj["url"] = comment.url
      +                obj["id"] = comment.id
      +                obj["body"] = comment.body
      +                # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
      +                self._comments.append(obj)
      +        return self._comments
      +
      +    @property
      +    def guid(self):
      +        return self.repo.fullname + "_" + str(self._ddict["number"])
      +
      +    @property
      +    def number(self):
      +        return int(self._ddict["number"])
      +
      +    @property
      +    def title(self):
      +        return self._ddict["title"]
      +
      +    @property
      +    def body(self):
      +        return self._ddict["body"]
      +
      +    @body.setter
      +    def body(self, val):
      +        self._ddict["body"] = val
      +        try:
      +            self.api.edit(body=self._ddict["body"])
      +        except Exception as e:
      +            self._log_error("Failed to update the issue body: %s" % e)
      +
      +    @property
      +    def time(self):
      +        return self._ddict["time"]
      +
      +    @property
      +    def url(self):
      +        return self._ddict["url"]
      +
      +    @property
      +    def assignee(self):
      +        return self._ddict["assignee"]
      +
      +    @property
      +    def labels(self):
      +        # we return a copy so changing the list doesn't actually change the
      +        # ddict value
      +        return self._ddict["labels"][:]
      +
      +    @property
      +    def id(self):
      +        return self._ddict["id"]
      +
      +    @labels.setter
      +    def labels(self, val):
      +        # check if all are already in labels, if yes nothing to do
      +        if len(val) == len(self._ddict["labels"]):
      +            self._ddict["labels"].sort()
      +            val.sort()
      +            if val == self._ddict["labels"]:
      +                return
      +        self._ddict["labels"] = val
      +        toset = [self.repo.getLabel(item) for item in self._ddict["labels"]]
      +        self.api.set_labels(*toset)
      +
      +    @property
      +    def milestone(self):
      +        return self._ddict["milestone"]
      +
      +    @property
      +    def state(self):
      +        states = []
      +        if not self.is_open:
      +            return "closed"
      +
      +        for label in self.labels:
      +            if label.startswith("state"):
      +                states.append(label)
      +        if len(states) == 1:
      +            return states[0][len("state") :].strip("_")
      +        elif len(states) > 1:
      +            self.state = "question"
      +        else:
      +            return ""
      +
      +    @state.setter
      +    def state(self, val):
      +        return self._setLabels(val, "state")
      +
      +    @property
      +    def is_open(self):
      +        return self._ddict["open"]
      +
      +    @property
      +    def type(self):
      +        items = []
      +        for label in self.labels:
      +            if label.startswith("type"):
      +                items.append(label)
      +        if len(items) == 1:
      +            return items[0].partition("_")[-1]
      +
      +        return ""
      +
      +    @type.setter
      +    def type(self, val):
      +        return self._setLabels(val, "type")
      +
      +    @property
      +    def priority(self):
      +        items = []
      +        for label in self.labels:
      +            if label.startswith("priority"):
      +                items.append(label)
      +        if len(items) == 1:
      +            return items[0].partition("_")[-1]
      +        else:
      +            self.priority = "normal"
      +            return self.priority
      +
      +    @priority.setter
      +    def priority(self, val):
      +        return self._setLabels(val, "priority")
      +
      +    @property
      +    def process(self):
      +        items = []
      +        for label in self.labels:
      +            if label.startswith("process"):
      +                items.append(label)
      +        if len(items) == 1:
      +            return items[0][len("process") :].strip("_")
      +        else:
      +            return ""
      +
      +    @process.setter
      +    def process(self, val):
      +        return self._setLabels(val, "process")
      +
      +    def _setLabels(self, val, category):
      +        if val is None or val == "":
      +            return
      +
      +        if val.startswith(category):
      +            _, _, val = val.partition("_")
      +
      +        val = val.strip("_")
      +        val = val.lower()
      +
      +        val = "%s_%s" % (category, val)
      +
      +        if val not in self.repo.labelnames:
      +            self.repo.labelnames.sort()
      +            llist = ",".join(self.repo.labelnames)
      +            raise Exception(
      +                "Label needs to be in list:%s (is understood labels in this repo on github), now is: '%s'"
      +                % (llist, val)
      +            )
      +
      +        # make sure there is only 1
      +        labels2set = self.labels
      +        items = []
      +        for label in self.labels:
      +            if label.startswith(category):
      +                items.append(label)
      +        if len(items) == 1 and val in items:
      +            return
      +        for item in items:
      +            labels2set.pop(labels2set.index(item))
      +        if val is not None or val != "":
      +            labels2set.append(val)
      +        self.labels = labels2set
      +
      +    def load(self):
      +
      +        self._ddict = {}
      +
      +        # check labels
      +        labels = [item.name for item in self.api.labels]  # are the names
      +        newlabels = []
      +        for label in labels:
      +            if label not in self.repo.labelnames:
      +                if label in replacelabels:
      +                    if replacelabels[label] not in newlabels:
      +                        newlabels.append(replacelabels[label])
      +            else:
      +                if label not in newlabels:
      +                    newlabels.append(label)
      +
      +        if labels != newlabels:
      +            self._log_info("change label:%s for %s" % (labels, self.api.title))
      +            labels2set = [self.repo.getLabel(item) for item in newlabels]
      +            self.api.set_labels(*labels2set)
      +            labels = newlabels
      +
      +        self._ddict["labels"] = labels
      +        self._ddict["id"] = self.api.id
      +        self._ddict["url"] = self.api.html_url
      +        self._ddict["number"] = self.api.number
      +        self._ddict["open"] = self.api.state == "open"
      +
      +        self._ddict["assignee"] = self.repo.client.getUserLogin(githubObj=self.api.assignee)
      +        self._ddict["state"] = self.api.state
      +        self._ddict["title"] = self.api.title
      +
      +        self._ddict["body"] = self.api.body
      +
      +        # self._ddict["time"] = j.data.time.any2HRDateTime([self.api.last_modified, self.api.created_at])
      +
      +        self._log_debug("LOAD:%s %s" % (self.repo.fullname, self._ddict["title"]))
      +
      +        if self.api.milestone is None:
      +            self._ddict["milestone"] = ""
      +        else:
      +            ms = RepoMilestone(repo=self.repo, githubObj=self.api.milestone)
      +            self._ddict["milestone"] = "%s:%s" % (ms.number, ms.title)
      +
      +    @property
      +    def todo(self):
      +        if "_todo" not in self.__dict__:
      +            todo = []
      +            if self.body is not None:
      +                for line in self.body.split("\n"):
      +                    if line.startswith("!! "):
      +                        todo.append(line.strip().strip("!! "))
      +            for comment in self.comments:
      +                for line in comment["body"].split("\n"):
      +                    if line.startswith("!! "):
      +                        todo.append(line.strip().strip("!! "))
      +            self._todo = todo
      +        return self._todo
      +
      +    @property
      +    def istask(self):
      +        if self.type == "task" or self.title.lower().endswith("task"):
      +            return True
      +        return False
      +
      +    def __str__(self):
      +        return "issue:%s" % self.title
      +
      +    __repr__ = __str__
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var api
      +
      +
      +
      + +Expand source code + +
      @property
      +def api(self):
      +    if self._githubObj is None:
      +        self._githubObj = self.repo.api.get_issue(self.number)
      +    return self._githubObj
      +
      +
      +
      var assignee
      +
      +
      +
      + +Expand source code + +
      @property
      +def assignee(self):
      +    return self._ddict["assignee"]
      +
      +
      +
      var body
      +
      +
      +
      + +Expand source code + +
      @property
      +def body(self):
      +    return self._ddict["body"]
      +
      +
      +
      var comments
      +
      +
      +
      + +Expand source code + +
      @property
      +def comments(self):
      +    if self._comments is not None:
      +        return self._comments
      +
      +    with self._lock:
      +        if self._comments is None:
      +            self._log_debug("Loading comments for issue: %s" % self.number)
      +            self._comments = []
      +            for comment in self.api.get_comments():
      +                obj = {}
      +                user = self.repo.client.getUserLogin(githubObj=comment.user)
      +                obj["user"] = user
      +                obj["url"] = comment.url
      +                obj["id"] = comment.id
      +                obj["body"] = comment.body
      +                obj["user_id"] = comment.user.id
      +                # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
      +                self._comments.append(obj)
      +    return self._comments
      +
      +
      +
      var ddict
      +
      +
      +
      + +Expand source code + +
      @property
      +def ddict(self):
      +    if self._ddict == {}:
      +        # no dict yet, fetch from github
      +        self.load()
      +    # we lazy load the comments. so it's only loaded when accesses
      +    self._ddict["comments"] = self.comments
      +    return self._ddict
      +
      +
      +
      var guid
      +
      +
      +
      + +Expand source code + +
      @property
      +def guid(self):
      +    return self.repo.fullname + "_" + str(self._ddict["number"])
      +
      +
      +
      var id
      +
      +
      +
      + +Expand source code + +
      @property
      +def id(self):
      +    return self._ddict["id"]
      +
      +
      +
      var is_open
      +
      +
      +
      + +Expand source code + +
      @property
      +def is_open(self):
      +    return self._ddict["open"]
      +
      +
      +
      var istask
      +
      +
      +
      + +Expand source code + +
      @property
      +def istask(self):
      +    if self.type == "task" or self.title.lower().endswith("task"):
      +        return True
      +    return False
      +
      +
      +
      var labels
      +
      +
      +
      + +Expand source code + +
      @property
      +def labels(self):
      +    # we return a copy so changing the list doesn't actually change the
      +    # ddict value
      +    return self._ddict["labels"][:]
      +
      +
      +
      var milestone
      +
      +
      +
      + +Expand source code + +
      @property
      +def milestone(self):
      +    return self._ddict["milestone"]
      +
      +
      +
      var number
      +
      +
      +
      + +Expand source code + +
      @property
      +def number(self):
      +    return int(self._ddict["number"])
      +
      +
      +
      var priority
      +
      +
      +
      + +Expand source code + +
      @property
      +def priority(self):
      +    items = []
      +    for label in self.labels:
      +        if label.startswith("priority"):
      +            items.append(label)
      +    if len(items) == 1:
      +        return items[0].partition("_")[-1]
      +    else:
      +        self.priority = "normal"
      +        return self.priority
      +
      +
      +
      var process
      +
      +
      +
      + +Expand source code + +
      @property
      +def process(self):
      +    items = []
      +    for label in self.labels:
      +        if label.startswith("process"):
      +            items.append(label)
      +    if len(items) == 1:
      +        return items[0][len("process") :].strip("_")
      +    else:
      +        return ""
      +
      +
      +
      var state
      +
      +
      +
      + +Expand source code + +
      @property
      +def state(self):
      +    states = []
      +    if not self.is_open:
      +        return "closed"
      +
      +    for label in self.labels:
      +        if label.startswith("state"):
      +            states.append(label)
      +    if len(states) == 1:
      +        return states[0][len("state") :].strip("_")
      +    elif len(states) > 1:
      +        self.state = "question"
      +    else:
      +        return ""
      +
      +
      +
      var time
      +
      +
      +
      + +Expand source code + +
      @property
      +def time(self):
      +    return self._ddict["time"]
      +
      +
      +
      var title
      +
      +
      +
      + +Expand source code + +
      @property
      +def title(self):
      +    return self._ddict["title"]
      +
      +
      +
      var todo
      +
      +
      +
      + +Expand source code + +
      @property
      +def todo(self):
      +    if "_todo" not in self.__dict__:
      +        todo = []
      +        if self.body is not None:
      +            for line in self.body.split("\n"):
      +                if line.startswith("!! "):
      +                    todo.append(line.strip().strip("!! "))
      +        for comment in self.comments:
      +            for line in comment["body"].split("\n"):
      +                if line.startswith("!! "):
      +                    todo.append(line.strip().strip("!! "))
      +        self._todo = todo
      +    return self._todo
      +
      +
      +
      var type
      +
      +
      +
      + +Expand source code + +
      @property
      +def type(self):
      +    items = []
      +    for label in self.labels:
      +        if label.startswith("type"):
      +            items.append(label)
      +    if len(items) == 1:
      +        return items[0].partition("_")[-1]
      +
      +    return ""
      +
      +
      +
      var url
      +
      +
      +
      + +Expand source code + +
      @property
      +def url(self):
      +    return self._ddict["url"]
      +
      +
      +
      +

      Methods

      +
      +
      +def load(self) +
      +
      +
      +
      + +Expand source code + +
      def load(self):
      +
      +    self._ddict = {}
      +
      +    # check labels
      +    labels = [item.name for item in self.api.labels]  # are the names
      +    newlabels = []
      +    for label in labels:
      +        if label not in self.repo.labelnames:
      +            if label in replacelabels:
      +                if replacelabels[label] not in newlabels:
      +                    newlabels.append(replacelabels[label])
      +        else:
      +            if label not in newlabels:
      +                newlabels.append(label)
      +
      +    if labels != newlabels:
      +        self._log_info("change label:%s for %s" % (labels, self.api.title))
      +        labels2set = [self.repo.getLabel(item) for item in newlabels]
      +        self.api.set_labels(*labels2set)
      +        labels = newlabels
      +
      +    self._ddict["labels"] = labels
      +    self._ddict["id"] = self.api.id
      +    self._ddict["url"] = self.api.html_url
      +    self._ddict["number"] = self.api.number
      +    self._ddict["open"] = self.api.state == "open"
      +
      +    self._ddict["assignee"] = self.repo.client.getUserLogin(githubObj=self.api.assignee)
      +    self._ddict["state"] = self.api.state
      +    self._ddict["title"] = self.api.title
      +
      +    self._ddict["body"] = self.api.body
      +
      +    # self._ddict["time"] = j.data.time.any2HRDateTime([self.api.last_modified, self.api.created_at])
      +
      +    self._log_debug("LOAD:%s %s" % (self.repo.fullname, self._ddict["title"]))
      +
      +    if self.api.milestone is None:
      +        self._ddict["milestone"] = ""
      +    else:
      +        ms = RepoMilestone(repo=self.repo, githubObj=self.api.milestone)
      +        self._ddict["milestone"] = "%s:%s" % (ms.number, ms.title)
      +
      +
      +
      +def reload_comments(self) +
      +
      +
      +
      + +Expand source code + +
      def reload_comments(self):
      +    with self._lock:
      +        self._comments = []
      +        for comment in self.api.get_comments():
      +            obj = {}
      +            user = self.repo.client.getUserLogin(githubObj=comment.user)
      +            obj["user"] = user
      +            obj["url"] = comment.url
      +            obj["id"] = comment.id
      +            obj["body"] = comment.body
      +            # obj["time"] = j.data.time.any2HRDateTime([comment.last_modified, comment.created_at])
      +            self._comments.append(obj)
      +    return self._comments
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/github/milestone.html b/docs/api/jumpscale/clients/github/milestone.html new file mode 100644 index 000000000..6b7e8300f --- /dev/null +++ b/docs/api/jumpscale/clients/github/milestone.html @@ -0,0 +1,385 @@ + + + + + + +jumpscale.clients.github.milestone API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.github.milestone

      +
      +
      +
      + +Expand source code + +
      from jumpscale.loader import j
      +from .base import base
      +
      +
      +class RepoMilestone(base):
      +    """
      +    milestone as defined on 1 specific repo
      +    """
      +
      +    def __init__(self, repo, githubObj=None):
      +        base.__init__(self)
      +        self._ddict = {}
      +        self._githubObj = githubObj
      +        if githubObj is not None:
      +            self.load()
      +        self.repo = repo
      +
      +    # @property
      +    # def api(self):
      +    #     if self._githubObj is None:
      +    #         j.application.break_into_jshell("DEBUG NOW get api for milestone")
      +    #     return self._githubObj
      +
      +    def load(self):
      +        self._ddict = {}
      +        #self._ddict["deadline"] = j.data.time.any2HRDateTime(self.api.due_on)
      +        self._ddict["id"] = self.api.id
      +        self._ddict["url"] = self.api.url
      +        self._ddict["title"] = self.api.title
      +        self._ddict["body"] = self.api.description
      +        self._ddict["number"] = self.api.number
      +
      +    @property
      +    def title(self):
      +        return self._ddict["title"]
      +
      +    @title.setter
      +    def title(self, val):
      +        self._ddict["title"] = val
      +        self.api.edit(title=val)
      +
      +    @property
      +    def ddict(self):
      +        if not self._ddict:
      +            # no dict yet, fetch from github
      +            self.load()
      +        return self._ddict
      +
      +
      +
      +    # synonym to let the tags of super class work
      +    @property
      +    def body(self):
      +        return self._ddict["body"]
      +
      +    @body.setter
      +    def body(self, val):
      +        if self._ddict["body"] != val:
      +            self._ddict["body"] = val
      +            self.api.edit(self.title, description=val)
      +
      +    # @property
      +    # def deadline(self):
      +    #     return self._ddict["deadline"]
      +
      +    # @deadline.setter
      +    # def deadline(self, val):
      +    #     #due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(val)))
      +
      +    #     self._ddict["deadline"] = val
      +    #     self.api.edit(title=self.title)
      +
      +    @property
      +    def id(self):
      +        return self._ddict["id"]
      +
      +    @property
      +    def url(self):
      +        return self._ddict["url"]
      +
      +    @property
      +    def number(self):
      +        return self._ddict["number"]
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class RepoMilestone +(repo, githubObj=None) +
      +
      +

      milestone as defined on 1 specific repo

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class RepoMilestone(base):
      +    """
      +    milestone as defined on 1 specific repo
      +    """
      +
      +    def __init__(self, repo, githubObj=None):
      +        base.__init__(self)
      +        self._ddict = {}
      +        self._githubObj = githubObj
      +        if githubObj is not None:
      +            self.load()
      +        self.repo = repo
      +
      +    # @property
      +    # def api(self):
      +    #     if self._githubObj is None:
      +    #         j.application.break_into_jshell("DEBUG NOW get api for milestone")
      +    #     return self._githubObj
      +
      +    def load(self):
      +        self._ddict = {}
      +        #self._ddict["deadline"] = j.data.time.any2HRDateTime(self.api.due_on)
      +        self._ddict["id"] = self.api.id
      +        self._ddict["url"] = self.api.url
      +        self._ddict["title"] = self.api.title
      +        self._ddict["body"] = self.api.description
      +        self._ddict["number"] = self.api.number
      +
      +    @property
      +    def title(self):
      +        return self._ddict["title"]
      +
      +    @title.setter
      +    def title(self, val):
      +        self._ddict["title"] = val
      +        self.api.edit(title=val)
      +
      +    @property
      +    def ddict(self):
      +        if not self._ddict:
      +            # no dict yet, fetch from github
      +            self.load()
      +        return self._ddict
      +
      +
      +
      +    # synonym to let the tags of super class work
      +    @property
      +    def body(self):
      +        return self._ddict["body"]
      +
      +    @body.setter
      +    def body(self, val):
      +        if self._ddict["body"] != val:
      +            self._ddict["body"] = val
      +            self.api.edit(self.title, description=val)
      +
      +    # @property
      +    # def deadline(self):
      +    #     return self._ddict["deadline"]
      +
      +    # @deadline.setter
      +    # def deadline(self, val):
      +    #     #due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(val)))
      +
      +    #     self._ddict["deadline"] = val
      +    #     self.api.edit(title=self.title)
      +
      +    @property
      +    def id(self):
      +        return self._ddict["id"]
      +
      +    @property
      +    def url(self):
      +        return self._ddict["url"]
      +
      +    @property
      +    def number(self):
      +        return self._ddict["number"]
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var body
      +
      +
      +
      + +Expand source code + +
      @property
      +def body(self):
      +    return self._ddict["body"]
      +
      +
      +
      var ddict
      +
      +
      +
      + +Expand source code + +
      @property
      +def ddict(self):
      +    if not self._ddict:
      +        # no dict yet, fetch from github
      +        self.load()
      +    return self._ddict
      +
      +
      +
      var id
      +
      +
      +
      + +Expand source code + +
      @property
      +def id(self):
      +    return self._ddict["id"]
      +
      +
      +
      var number
      +
      +
      +
      + +Expand source code + +
      @property
      +def number(self):
      +    return self._ddict["number"]
      +
      +
      +
      var title
      +
      +
      +
      + +Expand source code + +
      @property
      +def title(self):
      +    return self._ddict["title"]
      +
      +
      +
      var url
      +
      +
      +
      + +Expand source code + +
      @property
      +def url(self):
      +    return self._ddict["url"]
      +
      +
      +
      +

      Methods

      +
      +
      +def load(self) +
      +
      +
      +
      + +Expand source code + +
      def load(self):
      +    self._ddict = {}
      +    #self._ddict["deadline"] = j.data.time.any2HRDateTime(self.api.due_on)
      +    self._ddict["id"] = self.api.id
      +    self._ddict["url"] = self.api.url
      +    self._ddict["title"] = self.api.title
      +    self._ddict["body"] = self.api.description
      +    self._ddict["number"] = self.api.number
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/github/repo.html b/docs/api/jumpscale/clients/github/repo.html new file mode 100644 index 000000000..45c731ab3 --- /dev/null +++ b/docs/api/jumpscale/clients/github/repo.html @@ -0,0 +1,1673 @@ + + + + + + +jumpscale.clients.github.repo API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.github.repo

      +
      +
      +
      + +Expand source code + +
      import base64
      +import copy
      +import threading
      +
      +# import collections
      +import urllib
      +
      +from gevent import sleep
      +from jumpscale.clients.base import Client
      +from jumpscale.core.base import Base, fields
      +from jumpscale.loader import j
      +
      +from github.GithubException import UnknownObjectException
      +
      +from .base import replacelabels
      +from .helper import retry
      +from .issue import Issue
      +from .milestone import RepoMilestone
      +
      +
      +class GithubRepo:
      +    TYPES = ["story", "ticket", "task", "bug", "feature", "question", "monitor", "unknown"]
      +    PRIORITIES = ["critical", "urgent", "normal", "minor"]
      +
      +    STATES = ["new", "accepted", "question", "inprogress", "verification", "closed"]
      +
      +    def __init__(self, client, fullname):
      +        self.client = client
      +        self.fullname = fullname
      +        self._repoclient = None
      +        self._labels = None
      +        self._issues = None
      +        self._lock = threading.RLock()
      +        self._milestones = None
      +
      +    def _log_info(self, s):
      +        pass
      +
      +    @property
      +    def api(self):
      +        if self._repoclient is None:
      +            self._repoclient = self.client.get_repo(self.fullname)
      +        return self._repoclient
      +
      +    @property
      +    def name(self):
      +        return self.fullname.split("/", 1)[-1]
      +
      +    @property
      +    def type(self):
      +        if self.name in ["home"]:
      +            return "home"
      +        elif self.name.startswith("proj"):
      +            return "proj"
      +        elif self.name.startswith("org_"):
      +            return "org"
      +        elif self.name.startswith("www"):
      +            return "www"
      +        elif self.name.startswith("doc"):
      +            return "doc"
      +        elif self.name.startswith("cockpit"):
      +            return "cockpit"
      +        else:
      +            return "code"
      +
      +    @property
      +    def labelnames(self):
      +        return [item.name for item in self.labels]
      +
      +    @property
      +    def labels(self):
      +        with self._lock:
      +            if self._labels is None:
      +                self._labels = [item for item in self.api.get_labels()]
      +
      +        return self._labels
      +
      +    @property
      +    def branches(self):
      +        """list of `Branch` objects"""
      +        return list(self.api.get_branches())
      +
      +    @property
      +    def stories(self):
      +        # walk overall issues find the stories (based on type)
      +        # only for home type repo, otherwise return []
      +        return self.issues_by_type("story")
      +
      +    @property
      +    def tasks(self):
      +        # walk overall issues find the stories (based on type)
      +        # only for home type repo, otherwise return []
      +        return self.issues_by_type("task")
      +
      +    def labelsSet(self, labels2set, ignoreDelete=["p_"], delete=True):
      +        """
      +        @param ignore all labels starting with ignore will not be deleted
      +        """
      +
      +        for item in labels2set:
      +            if not isinstance(item, str):
      +                raise Exception("Labels to set need to be in string format, found:%s" % labels2set)
      +
      +        # walk over github existing labels
      +        labelstowalk = copy.copy(self.labels)
      +        for item in labelstowalk:
      +            name = item.name.lower()
      +            if name not in labels2set:
      +                # label in repo does not correspond to label we need
      +                if name in replacelabels:
      +                    nameNew = replacelabels[item.name.lower()]
      +                    if nameNew not in self.labelnames:
      +                        color = self.get_color(name)
      +                        self._log_info(
      +                            "change label in repo: %s oldlabel:'%s' to:'%s' color:%s"
      +                            % (self.fullname, item.name, nameNew, color)
      +                        )
      +                        item.edit(nameNew, color)
      +                        self._labels = None
      +                else:
      +                    # no replacement
      +                    name = "type_unknown"
      +                    color = self.get_color(name)
      +                    try:
      +                        item.edit(name, color)
      +                    except BaseException:
      +                        item.delete()
      +                    self._labels = None
      +
      +        # walk over new labels we need to set
      +        for name in labels2set:
      +            if name not in self.labelnames:
      +                # does not exist yet in repo
      +                color = self.get_color(name)
      +                self._log_info("create label: %s %s %s" % (self.fullname, name, color))
      +                self.api.create_label(name, color)
      +                self._labels = None
      +
      +        name = ""
      +
      +        if delete:
      +            labelstowalk = copy.copy(self.labels)
      +            for item in labelstowalk:
      +                if item.name not in labels2set:
      +                    self._log_info("delete label: %s %s" % (self.fullname, item.name))
      +                    ignoreDeleteDo = False
      +                    for filteritem in ignoreDelete:
      +                        if item.name.startswith(filteritem):
      +                            ignoreDeleteDo = True
      +                    if ignoreDeleteDo is False:
      +                        item.delete()
      +                    self._labels = None
      +
      +        # check the colors
      +        labelstowalk = copy.copy(self.labels)
      +        for item in labelstowalk:
      +            # we recognise the label
      +            self._log_info("check color of repo:%s labelname:'%s'" % (self.fullname, item.name))
      +            color = self.get_color(item.name)
      +            if item.color != color:
      +                self._log_info("change label color for repo %s %s" % (item.name, color))
      +                item.edit(item.name, color)
      +                self._labels = None
      +
      +    def getlabel(self, name):
      +        for item in self.labels:
      +            self._log_info("%s:look for name:'%s'" % (item.name, name))
      +            if item.name == name:
      +                return item
      +        raise Exception("Dit not find label: '%s'" % name)
      +
      +    def get_issue_from_markdown(self, issueNumber, markdown):
      +        i = self.get_issue(issueNumber, False)
      +        i._loadMD(markdown)
      +        self.issues.append(i)
      +        return i
      +
      +    def get_issue(self, issueNumber, die=True):
      +        for issue in self.issues:
      +            if issue.number == issueNumber:
      +                return issue
      +        # not found in cache, try to load from github
      +        github_issue = self.api.get_issue(issueNumber)
      +
      +        if github_issue:
      +            issue = Issue(repo=self, githubObj=github_issue)
      +            self._issues.append(issue)
      +            return issue
      +
      +        if die:
      +            raise Exception("cannot find issue:%s in repo:%s" % (issueNumber, self))
      +        else:
      +            i = Issue(self)
      +            i._ddict["number"] = issueNumber
      +            return i
      +
      +    def issues_by_type(self, *types):
      +        """
      +        filter is method which takes  issue as argument and returns True or False to include
      +        """
      +        issues = []
      +        for issue in self.issues:
      +            if issue.type in types:
      +                issues.append(issue)
      +
      +        return issues
      +
      +    def issues_by_state(self, filter=None):
      +        """
      +        filter is method which takes  issue as argument and returns True or False to include
      +        """
      +        res = {}
      +        for item in self.states:
      +            res[item] = []
      +            for issue in self.issues:
      +                if issue.state == item:
      +                    if filter is None or filter(issue):
      +                        res[item].append(issue)
      +        return res
      +
      +    def issues_by_priority(self, filter=None):
      +        """
      +        filter is method which takes  issue as argument and returns True or False to include
      +        """
      +        res = {}
      +        for item in self.priorities:
      +            res[item] = []
      +            for issue in self.issues:
      +                if issue.priority == item:
      +                    if filter is None or filter(issue):
      +                        res[item].append(issue)
      +        return res
      +
      +    def issues_by_type_state(self, filter=None, collapsepriority=True):
      +        """
      +        filter is method which takes  issue as argument and returns True or False to include
      +        returns dict of dict keys: type, state and then issues sorted following priority
      +        """
      +        res = {}
      +        for type in self.types:
      +            res[type] = {}
      +            for state in self.states:
      +                res[type][state] = {}
      +                for priority in self.priorities:
      +                    res[type][state][priority] = []
      +                    for issue in self.issues:
      +                        if issue.type == type and issue.state == state:
      +                            if filter is None or filter(issue):
      +                                res[type][state][priority].append(issue)
      +                if collapsepriority:
      +                    # sort the issues following priority
      +                    temp = res[type][state]
      +                    res[type][state] = []
      +                    for priority in self.priorities:
      +                        for subitem in temp[priority]:
      +                            res[type][state].append(subitem)
      +        return res
      +
      +    @property
      +    def types(self):
      +        return GithubRepo.TYPES
      +
      +    @property
      +    def priorities(self):
      +        return GithubRepo.PRIORITIES
      +
      +    @property
      +    def states(self):
      +        return GithubRepo.STATES
      +
      +    @property
      +    def milestones(self):
      +        if self._milestones is None:
      +            self._milestones = [RepoMilestone(self, x) for x in self.api.get_milestones()]
      +
      +        return self._milestones
      +
      +    @property
      +    def milestone_titles(self):
      +        return [item.title for item in self.milestones]
      +
      +    @property
      +    def milestone_names(self):
      +        return [item.name for item in self.milestones]
      +
      +    def get_milestone(self, name, die=True):
      +        name = name.strip()
      +        if name == "":
      +            raise Exception("Name cannot be empty.")
      +        for item in self.milestones:
      +            if name == item.name.strip() or name == item.title.strip():
      +                return item
      +        if die:
      +            raise Exception("Could not find milestone with name:%s" % name)
      +        else:
      +            return None
      +
      +    @retry
      +    def create_milestone(self, name, title, description="", deadline="", owner=""):
      +        self._log_info('Attempt to create milestone "%s" [%s] deadline %s' % (name, title, deadline))
      +
      +        def getBody(descr, name, owner):
      +            out = "%s\n\n" % descr
      +            out += "## name:%s\n" % name
      +            out += "## owner:%s\n" % owner
      +            return out
      +
      +        ms = None
      +        for s in [name, title]:
      +            ms = self.get_milestone(s, die=False)
      +            if ms is not None:
      +                break
      +
      +        if ms is not None:
      +            if ms.title != title:
      +                ms.title = title
      +            # if ms.deadline != deadline:
      +            #     ms.deadline = deadline
      +            tocheck = getBody(description.strip(), name, owner)
      +            if ms.body.strip() != tocheck.strip():
      +                ms.body = tocheck
      +        else:
      +            # due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(deadline)))
      +            self._log_info("Create milestone on %s: %s" % (self, title))
      +            body = getBody(description.strip(), name, owner)
      +            # workaround for https://github.com/PyGithub/PyGithub/issues/396
      +            milestone = self.api.create_milestone(title=title, description=body)
      +            milestone.edit(title=title)
      +
      +            self._milestones.append(RepoMilestone(self, milestone))
      +
      +    def delete_milestone(self, name):
      +        if name.strip() == "":
      +            raise Exception("Name cannot be empty.")
      +        self._log_info("Delete milestone on %s: '%s'" % (self, name))
      +        try:
      +            ms = self.get_milestone(name)
      +            ms.api.delete()
      +            self._milestones = []
      +        except Exception:
      +            self._log_info("Milestone '%s' doesn't exist. no need to delete" % name)
      +
      +    def _labelsubset(self, cat):
      +        res = []
      +        for item in self.labels:
      +            if item.startswith(cat):
      +                item = item[len(cat) :].strip("_")
      +                res.append(item)
      +        res.sort()
      +        return res
      +
      +    def get_color(self, name):
      +
      +        # colors={'state_question':'fbca04',
      +        #  'priority_urgent':'d93f0b',
      +        #  'state_verification':'006b75',
      +        #  'priority_minor':'',
      +        #  'type_task':'',
      +        #  'type_feature':'',
      +        #  'process_wontfix':"ffffff",
      +        #  'priority_critical':"b60205",
      +        #  'state_inprogress':"e6e6e6",
      +        #  'priority_normal':"e6e6e6",
      +        #  'type_story':"ee9a00",
      +        #  'process_duplicate':"",
      +        #  'state_closed':"5319e7",
      +        #  'type_bug':"fc2929",
      +        #  'state_accepted':"0e8a16",
      +        #  'type_question':"fbca04",
      +        #  'state_new':"1d76db"}
      +
      +        if name.startswith("state"):
      +            return "c2e0c6"  # light green
      +
      +        if name.startswith("process"):
      +            return "d4c5f9"  # light purple
      +
      +        if name.startswith("type"):
      +            return "fef2c0"  # light yellow
      +
      +        if name in ("priority_critical", "task_no_estimation"):
      +            return "b60205"  # red
      +
      +        if name.startswith("priority_urgent"):
      +            return "d93f0b"
      +
      +        if name.startswith("priority"):
      +            return "f9d0c4"  # roze
      +
      +        return "ffffff"
      +
      +    @retry
      +    def set_file(self, path, content, message="update file"):
      +        """
      +        Creates or updates the file content at path with given content
      +        :param path: file path `README.md`
      +        :param content: Plain content of file
      +        :return:
      +        """
      +        bytes = content.encode()
      +        encoded = base64.encodebytes(bytes)
      +
      +        params = {"message": message, "content": encoded.decode()}
      +
      +        path = urllib.parse.quote(path)
      +        try:
      +            obj = self.api.get_contents(path)
      +            params["sha"] = obj.sha
      +            if base64.decodebytes(obj.content.encode()) == bytes:
      +                return
      +        except UnknownObjectException:
      +            pass
      +
      +        self._log_info('Updating file "%s"' % path)
      +        self.api._requester.requestJsonAndCheck("PUT", self.api.url + "/contents/" + path, input=params)
      +
      +    @property
      +    def issues(self):
      +        with self._lock:
      +            if self._issues is None:
      +                issues = []
      +                for item in self.api.get_issues(state="all"):
      +                    issues.append(Issue(self, githubObj=item))
      +
      +                self._issues = issues
      +
      +        return self._issues
      +
      +    def download_directory(self, src, download_dir, branch=None):
      +        dest = j.sals.fs.join_paths(download_dir, self.api.full_name)
      +        j.sals.fs.mkdirs(dest)
      +        branch = branch or self.api.default_branch
      +        contents = self.api.get_dir_contents(src, ref=branch)
      +
      +        for content in contents:
      +            if content.type == "dir":
      +                dir_path = j.sals.fs.join_paths(dest, content.path)
      +                j.sals.fs.mkdirs(dir_path)
      +                self.download_directory(content.path, download_dir, branch)
      +            else:
      +                file_path = j.sals.fs.join_paths(dest, content.path)
      +                j.sals.fs.mkdirs(j.sals.fs.dirname(file_path))
      +                file_content = self.api.get_contents(content.path, ref=branch)
      +                with open(file_path, "+w") as f:
      +                    f.write(base64.b64decode(file_content.content).decode())
      +
      +        return j.sals.fs.join_paths(dest, src)
      +
      +    def get_git_tree(self, sha_or_branch):
      +        """return a list of `GitTreeElement` for every element in source tree"""
      +        return self.api.get_git_tree(sha_or_branch).tree
      +
      +    def __str__(self):
      +        return "gitrepo:%s" % self.fullname
      +
      +    __repr__ = __str__
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class GithubRepo +(client, fullname) +
      +
      +
      +
      + +Expand source code + +
      class GithubRepo:
      +    TYPES = ["story", "ticket", "task", "bug", "feature", "question", "monitor", "unknown"]
      +    PRIORITIES = ["critical", "urgent", "normal", "minor"]
      +
      +    STATES = ["new", "accepted", "question", "inprogress", "verification", "closed"]
      +
      +    def __init__(self, client, fullname):
      +        self.client = client
      +        self.fullname = fullname
      +        self._repoclient = None
      +        self._labels = None
      +        self._issues = None
      +        self._lock = threading.RLock()
      +        self._milestones = None
      +
      +    def _log_info(self, s):
      +        pass
      +
      +    @property
      +    def api(self):
      +        if self._repoclient is None:
      +            self._repoclient = self.client.get_repo(self.fullname)
      +        return self._repoclient
      +
      +    @property
      +    def name(self):
      +        return self.fullname.split("/", 1)[-1]
      +
      +    @property
      +    def type(self):
      +        if self.name in ["home"]:
      +            return "home"
      +        elif self.name.startswith("proj"):
      +            return "proj"
      +        elif self.name.startswith("org_"):
      +            return "org"
      +        elif self.name.startswith("www"):
      +            return "www"
      +        elif self.name.startswith("doc"):
      +            return "doc"
      +        elif self.name.startswith("cockpit"):
      +            return "cockpit"
      +        else:
      +            return "code"
      +
      +    @property
      +    def labelnames(self):
      +        return [item.name for item in self.labels]
      +
      +    @property
      +    def labels(self):
      +        with self._lock:
      +            if self._labels is None:
      +                self._labels = [item for item in self.api.get_labels()]
      +
      +        return self._labels
      +
      +    @property
      +    def branches(self):
      +        """list of `Branch` objects"""
      +        return list(self.api.get_branches())
      +
      +    @property
      +    def stories(self):
      +        # walk overall issues find the stories (based on type)
      +        # only for home type repo, otherwise return []
      +        return self.issues_by_type("story")
      +
      +    @property
      +    def tasks(self):
      +        # walk overall issues find the stories (based on type)
      +        # only for home type repo, otherwise return []
      +        return self.issues_by_type("task")
      +
      +    def labelsSet(self, labels2set, ignoreDelete=["p_"], delete=True):
      +        """
      +        @param ignore all labels starting with ignore will not be deleted
      +        """
      +
      +        for item in labels2set:
      +            if not isinstance(item, str):
      +                raise Exception("Labels to set need to be in string format, found:%s" % labels2set)
      +
      +        # walk over github existing labels
      +        labelstowalk = copy.copy(self.labels)
      +        for item in labelstowalk:
      +            name = item.name.lower()
      +            if name not in labels2set:
      +                # label in repo does not correspond to label we need
      +                if name in replacelabels:
      +                    nameNew = replacelabels[item.name.lower()]
      +                    if nameNew not in self.labelnames:
      +                        color = self.get_color(name)
      +                        self._log_info(
      +                            "change label in repo: %s oldlabel:'%s' to:'%s' color:%s"
      +                            % (self.fullname, item.name, nameNew, color)
      +                        )
      +                        item.edit(nameNew, color)
      +                        self._labels = None
      +                else:
      +                    # no replacement
      +                    name = "type_unknown"
      +                    color = self.get_color(name)
      +                    try:
      +                        item.edit(name, color)
      +                    except BaseException:
      +                        item.delete()
      +                    self._labels = None
      +
      +        # walk over new labels we need to set
      +        for name in labels2set:
      +            if name not in self.labelnames:
      +                # does not exist yet in repo
      +                color = self.get_color(name)
      +                self._log_info("create label: %s %s %s" % (self.fullname, name, color))
      +                self.api.create_label(name, color)
      +                self._labels = None
      +
      +        name = ""
      +
      +        if delete:
      +            labelstowalk = copy.copy(self.labels)
      +            for item in labelstowalk:
      +                if item.name not in labels2set:
      +                    self._log_info("delete label: %s %s" % (self.fullname, item.name))
      +                    ignoreDeleteDo = False
      +                    for filteritem in ignoreDelete:
      +                        if item.name.startswith(filteritem):
      +                            ignoreDeleteDo = True
      +                    if ignoreDeleteDo is False:
      +                        item.delete()
      +                    self._labels = None
      +
      +        # check the colors
      +        labelstowalk = copy.copy(self.labels)
      +        for item in labelstowalk:
      +            # we recognise the label
      +            self._log_info("check color of repo:%s labelname:'%s'" % (self.fullname, item.name))
      +            color = self.get_color(item.name)
      +            if item.color != color:
      +                self._log_info("change label color for repo %s %s" % (item.name, color))
      +                item.edit(item.name, color)
      +                self._labels = None
      +
      +    def getlabel(self, name):
      +        for item in self.labels:
      +            self._log_info("%s:look for name:'%s'" % (item.name, name))
      +            if item.name == name:
      +                return item
      +        raise Exception("Dit not find label: '%s'" % name)
      +
      +    def get_issue_from_markdown(self, issueNumber, markdown):
      +        i = self.get_issue(issueNumber, False)
      +        i._loadMD(markdown)
      +        self.issues.append(i)
      +        return i
      +
      +    def get_issue(self, issueNumber, die=True):
      +        for issue in self.issues:
      +            if issue.number == issueNumber:
      +                return issue
      +        # not found in cache, try to load from github
      +        github_issue = self.api.get_issue(issueNumber)
      +
      +        if github_issue:
      +            issue = Issue(repo=self, githubObj=github_issue)
      +            self._issues.append(issue)
      +            return issue
      +
      +        if die:
      +            raise Exception("cannot find issue:%s in repo:%s" % (issueNumber, self))
      +        else:
      +            i = Issue(self)
      +            i._ddict["number"] = issueNumber
      +            return i
      +
      +    def issues_by_type(self, *types):
      +        """
      +        filter is method which takes  issue as argument and returns True or False to include
      +        """
      +        issues = []
      +        for issue in self.issues:
      +            if issue.type in types:
      +                issues.append(issue)
      +
      +        return issues
      +
      +    def issues_by_state(self, filter=None):
      +        """
      +        filter is method which takes  issue as argument and returns True or False to include
      +        """
      +        res = {}
      +        for item in self.states:
      +            res[item] = []
      +            for issue in self.issues:
      +                if issue.state == item:
      +                    if filter is None or filter(issue):
      +                        res[item].append(issue)
      +        return res
      +
      +    def issues_by_priority(self, filter=None):
      +        """
      +        filter is method which takes  issue as argument and returns True or False to include
      +        """
      +        res = {}
      +        for item in self.priorities:
      +            res[item] = []
      +            for issue in self.issues:
      +                if issue.priority == item:
      +                    if filter is None or filter(issue):
      +                        res[item].append(issue)
      +        return res
      +
      +    def issues_by_type_state(self, filter=None, collapsepriority=True):
      +        """
      +        filter is method which takes  issue as argument and returns True or False to include
      +        returns dict of dict keys: type, state and then issues sorted following priority
      +        """
      +        res = {}
      +        for type in self.types:
      +            res[type] = {}
      +            for state in self.states:
      +                res[type][state] = {}
      +                for priority in self.priorities:
      +                    res[type][state][priority] = []
      +                    for issue in self.issues:
      +                        if issue.type == type and issue.state == state:
      +                            if filter is None or filter(issue):
      +                                res[type][state][priority].append(issue)
      +                if collapsepriority:
      +                    # sort the issues following priority
      +                    temp = res[type][state]
      +                    res[type][state] = []
      +                    for priority in self.priorities:
      +                        for subitem in temp[priority]:
      +                            res[type][state].append(subitem)
      +        return res
      +
      +    @property
      +    def types(self):
      +        return GithubRepo.TYPES
      +
      +    @property
      +    def priorities(self):
      +        return GithubRepo.PRIORITIES
      +
      +    @property
      +    def states(self):
      +        return GithubRepo.STATES
      +
      +    @property
      +    def milestones(self):
      +        if self._milestones is None:
      +            self._milestones = [RepoMilestone(self, x) for x in self.api.get_milestones()]
      +
      +        return self._milestones
      +
      +    @property
      +    def milestone_titles(self):
      +        return [item.title for item in self.milestones]
      +
      +    @property
      +    def milestone_names(self):
      +        return [item.name for item in self.milestones]
      +
      +    def get_milestone(self, name, die=True):
      +        name = name.strip()
      +        if name == "":
      +            raise Exception("Name cannot be empty.")
      +        for item in self.milestones:
      +            if name == item.name.strip() or name == item.title.strip():
      +                return item
      +        if die:
      +            raise Exception("Could not find milestone with name:%s" % name)
      +        else:
      +            return None
      +
      +    @retry
      +    def create_milestone(self, name, title, description="", deadline="", owner=""):
      +        self._log_info('Attempt to create milestone "%s" [%s] deadline %s' % (name, title, deadline))
      +
      +        def getBody(descr, name, owner):
      +            out = "%s\n\n" % descr
      +            out += "## name:%s\n" % name
      +            out += "## owner:%s\n" % owner
      +            return out
      +
      +        ms = None
      +        for s in [name, title]:
      +            ms = self.get_milestone(s, die=False)
      +            if ms is not None:
      +                break
      +
      +        if ms is not None:
      +            if ms.title != title:
      +                ms.title = title
      +            # if ms.deadline != deadline:
      +            #     ms.deadline = deadline
      +            tocheck = getBody(description.strip(), name, owner)
      +            if ms.body.strip() != tocheck.strip():
      +                ms.body = tocheck
      +        else:
      +            # due = j.data.time.epoch2pythonDateTime(int(j.data.time.getEpochFuture(deadline)))
      +            self._log_info("Create milestone on %s: %s" % (self, title))
      +            body = getBody(description.strip(), name, owner)
      +            # workaround for https://github.com/PyGithub/PyGithub/issues/396
      +            milestone = self.api.create_milestone(title=title, description=body)
      +            milestone.edit(title=title)
      +
      +            self._milestones.append(RepoMilestone(self, milestone))
      +
      +    def delete_milestone(self, name):
      +        if name.strip() == "":
      +            raise Exception("Name cannot be empty.")
      +        self._log_info("Delete milestone on %s: '%s'" % (self, name))
      +        try:
      +            ms = self.get_milestone(name)
      +            ms.api.delete()
      +            self._milestones = []
      +        except Exception:
      +            self._log_info("Milestone '%s' doesn't exist. no need to delete" % name)
      +
      +    def _labelsubset(self, cat):
      +        res = []
      +        for item in self.labels:
      +            if item.startswith(cat):
      +                item = item[len(cat) :].strip("_")
      +                res.append(item)
      +        res.sort()
      +        return res
      +
      +    def get_color(self, name):
      +
      +        # colors={'state_question':'fbca04',
      +        #  'priority_urgent':'d93f0b',
      +        #  'state_verification':'006b75',
      +        #  'priority_minor':'',
      +        #  'type_task':'',
      +        #  'type_feature':'',
      +        #  'process_wontfix':"ffffff",
      +        #  'priority_critical':"b60205",
      +        #  'state_inprogress':"e6e6e6",
      +        #  'priority_normal':"e6e6e6",
      +        #  'type_story':"ee9a00",
      +        #  'process_duplicate':"",
      +        #  'state_closed':"5319e7",
      +        #  'type_bug':"fc2929",
      +        #  'state_accepted':"0e8a16",
      +        #  'type_question':"fbca04",
      +        #  'state_new':"1d76db"}
      +
      +        if name.startswith("state"):
      +            return "c2e0c6"  # light green
      +
      +        if name.startswith("process"):
      +            return "d4c5f9"  # light purple
      +
      +        if name.startswith("type"):
      +            return "fef2c0"  # light yellow
      +
      +        if name in ("priority_critical", "task_no_estimation"):
      +            return "b60205"  # red
      +
      +        if name.startswith("priority_urgent"):
      +            return "d93f0b"
      +
      +        if name.startswith("priority"):
      +            return "f9d0c4"  # roze
      +
      +        return "ffffff"
      +
      +    @retry
      +    def set_file(self, path, content, message="update file"):
      +        """
      +        Creates or updates the file content at path with given content
      +        :param path: file path `README.md`
      +        :param content: Plain content of file
      +        :return:
      +        """
      +        bytes = content.encode()
      +        encoded = base64.encodebytes(bytes)
      +
      +        params = {"message": message, "content": encoded.decode()}
      +
      +        path = urllib.parse.quote(path)
      +        try:
      +            obj = self.api.get_contents(path)
      +            params["sha"] = obj.sha
      +            if base64.decodebytes(obj.content.encode()) == bytes:
      +                return
      +        except UnknownObjectException:
      +            pass
      +
      +        self._log_info('Updating file "%s"' % path)
      +        self.api._requester.requestJsonAndCheck("PUT", self.api.url + "/contents/" + path, input=params)
      +
      +    @property
      +    def issues(self):
      +        with self._lock:
      +            if self._issues is None:
      +                issues = []
      +                for item in self.api.get_issues(state="all"):
      +                    issues.append(Issue(self, githubObj=item))
      +
      +                self._issues = issues
      +
      +        return self._issues
      +
      +    def download_directory(self, src, download_dir, branch=None):
      +        dest = j.sals.fs.join_paths(download_dir, self.api.full_name)
      +        j.sals.fs.mkdirs(dest)
      +        branch = branch or self.api.default_branch
      +        contents = self.api.get_dir_contents(src, ref=branch)
      +
      +        for content in contents:
      +            if content.type == "dir":
      +                dir_path = j.sals.fs.join_paths(dest, content.path)
      +                j.sals.fs.mkdirs(dir_path)
      +                self.download_directory(content.path, download_dir, branch)
      +            else:
      +                file_path = j.sals.fs.join_paths(dest, content.path)
      +                j.sals.fs.mkdirs(j.sals.fs.dirname(file_path))
      +                file_content = self.api.get_contents(content.path, ref=branch)
      +                with open(file_path, "+w") as f:
      +                    f.write(base64.b64decode(file_content.content).decode())
      +
      +        return j.sals.fs.join_paths(dest, src)
      +
      +    def get_git_tree(self, sha_or_branch):
      +        """return a list of `GitTreeElement` for every element in source tree"""
      +        return self.api.get_git_tree(sha_or_branch).tree
      +
      +    def __str__(self):
      +        return "gitrepo:%s" % self.fullname
      +
      +    __repr__ = __str__
      +
      +

      Class variables

      +
      +
      var PRIORITIES
      +
      +
      +
      +
      var STATES
      +
      +
      +
      +
      var TYPES
      +
      +
      +
      +
      +

      Instance variables

      +
      +
      var api
      +
      +
      +
      + +Expand source code + +
      @property
      +def api(self):
      +    if self._repoclient is None:
      +        self._repoclient = self.client.get_repo(self.fullname)
      +    return self._repoclient
      +
      +
      +
      var branches
      +
      +

      list of Branch objects

      +
      + +Expand source code + +
      @property
      +def branches(self):
      +    """list of `Branch` objects"""
      +    return list(self.api.get_branches())
      +
      +
      +
      var issues
      +
      +
      +
      + +Expand source code + +
      @property
      +def issues(self):
      +    with self._lock:
      +        if self._issues is None:
      +            issues = []
      +            for item in self.api.get_issues(state="all"):
      +                issues.append(Issue(self, githubObj=item))
      +
      +            self._issues = issues
      +
      +    return self._issues
      +
      +
      +
      var labelnames
      +
      +
      +
      + +Expand source code + +
      @property
      +def labelnames(self):
      +    return [item.name for item in self.labels]
      +
      +
      +
      var labels
      +
      +
      +
      + +Expand source code + +
      @property
      +def labels(self):
      +    with self._lock:
      +        if self._labels is None:
      +            self._labels = [item for item in self.api.get_labels()]
      +
      +    return self._labels
      +
      +
      +
      var milestone_names
      +
      +
      +
      + +Expand source code + +
      @property
      +def milestone_names(self):
      +    return [item.name for item in self.milestones]
      +
      +
      +
      var milestone_titles
      +
      +
      +
      + +Expand source code + +
      @property
      +def milestone_titles(self):
      +    return [item.title for item in self.milestones]
      +
      +
      +
      var milestones
      +
      +
      +
      + +Expand source code + +
      @property
      +def milestones(self):
      +    if self._milestones is None:
      +        self._milestones = [RepoMilestone(self, x) for x in self.api.get_milestones()]
      +
      +    return self._milestones
      +
      +
      +
      var name
      +
      +
      +
      + +Expand source code + +
      @property
      +def name(self):
      +    return self.fullname.split("/", 1)[-1]
      +
      +
      +
      var priorities
      +
      +
      +
      + +Expand source code + +
      @property
      +def priorities(self):
      +    return GithubRepo.PRIORITIES
      +
      +
      +
      var states
      +
      +
      +
      + +Expand source code + +
      @property
      +def states(self):
      +    return GithubRepo.STATES
      +
      +
      +
      var stories
      +
      +
      +
      + +Expand source code + +
      @property
      +def stories(self):
      +    # walk overall issues find the stories (based on type)
      +    # only for home type repo, otherwise return []
      +    return self.issues_by_type("story")
      +
      +
      +
      var tasks
      +
      +
      +
      + +Expand source code + +
      @property
      +def tasks(self):
      +    # walk overall issues find the stories (based on type)
      +    # only for home type repo, otherwise return []
      +    return self.issues_by_type("task")
      +
      +
      +
      var type
      +
      +
      +
      + +Expand source code + +
      @property
      +def type(self):
      +    if self.name in ["home"]:
      +        return "home"
      +    elif self.name.startswith("proj"):
      +        return "proj"
      +    elif self.name.startswith("org_"):
      +        return "org"
      +    elif self.name.startswith("www"):
      +        return "www"
      +    elif self.name.startswith("doc"):
      +        return "doc"
      +    elif self.name.startswith("cockpit"):
      +        return "cockpit"
      +    else:
      +        return "code"
      +
      +
      +
      var types
      +
      +
      +
      + +Expand source code + +
      @property
      +def types(self):
      +    return GithubRepo.TYPES
      +
      +
      +
      +

      Methods

      +
      +
      +def create_milestone(self, *args, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def wrapper(self, *args, **kwargs):
      +    for _ in range(6):
      +        try:
      +            result = function(self, *args, **kwargs)
      +            break
      +        except Exception as e:
      +            j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +            sleep(1)
      +    else:
      +        raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +    return result
      +
      +
      +
      +def delete_milestone(self, name) +
      +
      +
      +
      + +Expand source code + +
      def delete_milestone(self, name):
      +    if name.strip() == "":
      +        raise Exception("Name cannot be empty.")
      +    self._log_info("Delete milestone on %s: '%s'" % (self, name))
      +    try:
      +        ms = self.get_milestone(name)
      +        ms.api.delete()
      +        self._milestones = []
      +    except Exception:
      +        self._log_info("Milestone '%s' doesn't exist. no need to delete" % name)
      +
      +
      +
      +def download_directory(self, src, download_dir, branch=None) +
      +
      +
      +
      + +Expand source code + +
      def download_directory(self, src, download_dir, branch=None):
      +    dest = j.sals.fs.join_paths(download_dir, self.api.full_name)
      +    j.sals.fs.mkdirs(dest)
      +    branch = branch or self.api.default_branch
      +    contents = self.api.get_dir_contents(src, ref=branch)
      +
      +    for content in contents:
      +        if content.type == "dir":
      +            dir_path = j.sals.fs.join_paths(dest, content.path)
      +            j.sals.fs.mkdirs(dir_path)
      +            self.download_directory(content.path, download_dir, branch)
      +        else:
      +            file_path = j.sals.fs.join_paths(dest, content.path)
      +            j.sals.fs.mkdirs(j.sals.fs.dirname(file_path))
      +            file_content = self.api.get_contents(content.path, ref=branch)
      +            with open(file_path, "+w") as f:
      +                f.write(base64.b64decode(file_content.content).decode())
      +
      +    return j.sals.fs.join_paths(dest, src)
      +
      +
      +
      +def get_color(self, name) +
      +
      +
      +
      + +Expand source code + +
      def get_color(self, name):
      +
      +    # colors={'state_question':'fbca04',
      +    #  'priority_urgent':'d93f0b',
      +    #  'state_verification':'006b75',
      +    #  'priority_minor':'',
      +    #  'type_task':'',
      +    #  'type_feature':'',
      +    #  'process_wontfix':"ffffff",
      +    #  'priority_critical':"b60205",
      +    #  'state_inprogress':"e6e6e6",
      +    #  'priority_normal':"e6e6e6",
      +    #  'type_story':"ee9a00",
      +    #  'process_duplicate':"",
      +    #  'state_closed':"5319e7",
      +    #  'type_bug':"fc2929",
      +    #  'state_accepted':"0e8a16",
      +    #  'type_question':"fbca04",
      +    #  'state_new':"1d76db"}
      +
      +    if name.startswith("state"):
      +        return "c2e0c6"  # light green
      +
      +    if name.startswith("process"):
      +        return "d4c5f9"  # light purple
      +
      +    if name.startswith("type"):
      +        return "fef2c0"  # light yellow
      +
      +    if name in ("priority_critical", "task_no_estimation"):
      +        return "b60205"  # red
      +
      +    if name.startswith("priority_urgent"):
      +        return "d93f0b"
      +
      +    if name.startswith("priority"):
      +        return "f9d0c4"  # roze
      +
      +    return "ffffff"
      +
      +
      +
      +def get_git_tree(self, sha_or_branch) +
      +
      +

      return a list of GitTreeElement for every element in source tree

      +
      + +Expand source code + +
      def get_git_tree(self, sha_or_branch):
      +    """return a list of `GitTreeElement` for every element in source tree"""
      +    return self.api.get_git_tree(sha_or_branch).tree
      +
      +
      +
      +def get_issue(self, issueNumber, die=True) +
      +
      +
      +
      + +Expand source code + +
      def get_issue(self, issueNumber, die=True):
      +    for issue in self.issues:
      +        if issue.number == issueNumber:
      +            return issue
      +    # not found in cache, try to load from github
      +    github_issue = self.api.get_issue(issueNumber)
      +
      +    if github_issue:
      +        issue = Issue(repo=self, githubObj=github_issue)
      +        self._issues.append(issue)
      +        return issue
      +
      +    if die:
      +        raise Exception("cannot find issue:%s in repo:%s" % (issueNumber, self))
      +    else:
      +        i = Issue(self)
      +        i._ddict["number"] = issueNumber
      +        return i
      +
      +
      +
      +def get_issue_from_markdown(self, issueNumber, markdown) +
      +
      +
      +
      + +Expand source code + +
      def get_issue_from_markdown(self, issueNumber, markdown):
      +    i = self.get_issue(issueNumber, False)
      +    i._loadMD(markdown)
      +    self.issues.append(i)
      +    return i
      +
      +
      +
      +def get_milestone(self, name, die=True) +
      +
      +
      +
      + +Expand source code + +
      def get_milestone(self, name, die=True):
      +    name = name.strip()
      +    if name == "":
      +        raise Exception("Name cannot be empty.")
      +    for item in self.milestones:
      +        if name == item.name.strip() or name == item.title.strip():
      +            return item
      +    if die:
      +        raise Exception("Could not find milestone with name:%s" % name)
      +    else:
      +        return None
      +
      +
      +
      +def getlabel(self, name) +
      +
      +
      +
      + +Expand source code + +
      def getlabel(self, name):
      +    for item in self.labels:
      +        self._log_info("%s:look for name:'%s'" % (item.name, name))
      +        if item.name == name:
      +            return item
      +    raise Exception("Dit not find label: '%s'" % name)
      +
      +
      +
      +def issues_by_priority(self, filter=None) +
      +
      +

      filter is method which takes +issue as argument and returns True or False to include

      +
      + +Expand source code + +
      def issues_by_priority(self, filter=None):
      +    """
      +    filter is method which takes  issue as argument and returns True or False to include
      +    """
      +    res = {}
      +    for item in self.priorities:
      +        res[item] = []
      +        for issue in self.issues:
      +            if issue.priority == item:
      +                if filter is None or filter(issue):
      +                    res[item].append(issue)
      +    return res
      +
      +
      +
      +def issues_by_state(self, filter=None) +
      +
      +

      filter is method which takes +issue as argument and returns True or False to include

      +
      + +Expand source code + +
      def issues_by_state(self, filter=None):
      +    """
      +    filter is method which takes  issue as argument and returns True or False to include
      +    """
      +    res = {}
      +    for item in self.states:
      +        res[item] = []
      +        for issue in self.issues:
      +            if issue.state == item:
      +                if filter is None or filter(issue):
      +                    res[item].append(issue)
      +    return res
      +
      +
      +
      +def issues_by_type(self, *types) +
      +
      +

      filter is method which takes +issue as argument and returns True or False to include

      +
      + +Expand source code + +
      def issues_by_type(self, *types):
      +    """
      +    filter is method which takes  issue as argument and returns True or False to include
      +    """
      +    issues = []
      +    for issue in self.issues:
      +        if issue.type in types:
      +            issues.append(issue)
      +
      +    return issues
      +
      +
      +
      +def issues_by_type_state(self, filter=None, collapsepriority=True) +
      +
      +

      filter is method which takes +issue as argument and returns True or False to include +returns dict of dict keys: type, state and then issues sorted following priority

      +
      + +Expand source code + +
      def issues_by_type_state(self, filter=None, collapsepriority=True):
      +    """
      +    filter is method which takes  issue as argument and returns True or False to include
      +    returns dict of dict keys: type, state and then issues sorted following priority
      +    """
      +    res = {}
      +    for type in self.types:
      +        res[type] = {}
      +        for state in self.states:
      +            res[type][state] = {}
      +            for priority in self.priorities:
      +                res[type][state][priority] = []
      +                for issue in self.issues:
      +                    if issue.type == type and issue.state == state:
      +                        if filter is None or filter(issue):
      +                            res[type][state][priority].append(issue)
      +            if collapsepriority:
      +                # sort the issues following priority
      +                temp = res[type][state]
      +                res[type][state] = []
      +                for priority in self.priorities:
      +                    for subitem in temp[priority]:
      +                        res[type][state].append(subitem)
      +    return res
      +
      +
      +
      +def labelsSet(self, labels2set, ignoreDelete=['p_'], delete=True) +
      +
      +

      @param ignore all labels starting with ignore will not be deleted

      +
      + +Expand source code + +
      def labelsSet(self, labels2set, ignoreDelete=["p_"], delete=True):
      +    """
      +    @param ignore all labels starting with ignore will not be deleted
      +    """
      +
      +    for item in labels2set:
      +        if not isinstance(item, str):
      +            raise Exception("Labels to set need to be in string format, found:%s" % labels2set)
      +
      +    # walk over github existing labels
      +    labelstowalk = copy.copy(self.labels)
      +    for item in labelstowalk:
      +        name = item.name.lower()
      +        if name not in labels2set:
      +            # label in repo does not correspond to label we need
      +            if name in replacelabels:
      +                nameNew = replacelabels[item.name.lower()]
      +                if nameNew not in self.labelnames:
      +                    color = self.get_color(name)
      +                    self._log_info(
      +                        "change label in repo: %s oldlabel:'%s' to:'%s' color:%s"
      +                        % (self.fullname, item.name, nameNew, color)
      +                    )
      +                    item.edit(nameNew, color)
      +                    self._labels = None
      +            else:
      +                # no replacement
      +                name = "type_unknown"
      +                color = self.get_color(name)
      +                try:
      +                    item.edit(name, color)
      +                except BaseException:
      +                    item.delete()
      +                self._labels = None
      +
      +    # walk over new labels we need to set
      +    for name in labels2set:
      +        if name not in self.labelnames:
      +            # does not exist yet in repo
      +            color = self.get_color(name)
      +            self._log_info("create label: %s %s %s" % (self.fullname, name, color))
      +            self.api.create_label(name, color)
      +            self._labels = None
      +
      +    name = ""
      +
      +    if delete:
      +        labelstowalk = copy.copy(self.labels)
      +        for item in labelstowalk:
      +            if item.name not in labels2set:
      +                self._log_info("delete label: %s %s" % (self.fullname, item.name))
      +                ignoreDeleteDo = False
      +                for filteritem in ignoreDelete:
      +                    if item.name.startswith(filteritem):
      +                        ignoreDeleteDo = True
      +                if ignoreDeleteDo is False:
      +                    item.delete()
      +                self._labels = None
      +
      +    # check the colors
      +    labelstowalk = copy.copy(self.labels)
      +    for item in labelstowalk:
      +        # we recognise the label
      +        self._log_info("check color of repo:%s labelname:'%s'" % (self.fullname, item.name))
      +        color = self.get_color(item.name)
      +        if item.color != color:
      +            self._log_info("change label color for repo %s %s" % (item.name, color))
      +            item.edit(item.name, color)
      +            self._labels = None
      +
      +
      +
      +def set_file(self, *args, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def wrapper(self, *args, **kwargs):
      +    for _ in range(6):
      +        try:
      +            result = function(self, *args, **kwargs)
      +            break
      +        except Exception as e:
      +            j.logger.warning(f"Failed to execute {function.__name__} due to error: {str(e)}")
      +            sleep(1)
      +    else:
      +        raise j.exceptions.Runtime(f"Failed to execute {function.__name__} after multiple retries")
      +    return result
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/gogs/gogs.html b/docs/api/jumpscale/clients/gogs/gogs.html new file mode 100644 index 000000000..6bac6d99a --- /dev/null +++ b/docs/api/jumpscale/clients/gogs/gogs.html @@ -0,0 +1,334 @@ + + + + + + +jumpscale.clients.gogs.gogs API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.gogs.gogs

      +
      +
      +
      + +Expand source code + +
      from jumpscale.clients.base import Base, Client
      +from jumpscale.core.base import fields
      +
      +
      +class User(Base):
      +    username = fields.String(required=True)
      +    password = fields.Secret(required=True)
      +
      +
      +class Gogs(Client):
      +    access_token = fields.Secret(required=True)
      +    user = fields.Object(User)
      +    admins = fields.List(fields.String(required=True), required=True)
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class Gogs +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class Gogs(Client):
      +    access_token = fields.Secret(required=True)
      +    user = fields.Object(User)
      +    admins = fields.List(fields.String(required=True), required=True)
      +
      +

      Ancestors

      + +

      Instance variables

      +
      +
      var access_token
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var admins
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var user
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class User +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class User(Base):
      +    username = fields.String(required=True)
      +    password = fields.Secret(required=True)
      +
      +

      Ancestors

      +
        +
      • Base
      • +
      • types.SimpleNamespace
      • +
      +

      Instance variables

      +
      +
      var password
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var username
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/gogs/index.html b/docs/api/jumpscale/clients/gogs/index.html new file mode 100644 index 000000000..754879147 --- /dev/null +++ b/docs/api/jumpscale/clients/gogs/index.html @@ -0,0 +1,99 @@ + + + + + + +jumpscale.clients.gogs API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.clients.gogs

      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import StoredFactory
      +    from .gogs import Gogs
      +
      +    return StoredFactory(Gogs)
      +
      +
      +
      +

      Sub-modules

      +
      +
      jumpscale.clients.gogs.gogs
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import StoredFactory
      +    from .gogs import Gogs
      +
      +    return StoredFactory(Gogs)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/index.html b/docs/api/jumpscale/clients/index.html index deeba2ba2..070269d82 100644 --- a/docs/api/jumpscale/clients/index.html +++ b/docs/api/jumpscale/clients/index.html @@ -30,18 +30,68 @@

      Sub-modules

      +
      jumpscale.clients.btc_alpha
      +
      +
      +
      +
      jumpscale.clients.currencylayer
      +
      +

      JS-NG> fake = j.clients.currencylayer.new('fake') +
      +JS-NG> fake.cur2id_print() …

      +
      +
      jumpscale.clients.digitalocean
      +
      +
      +
      jumpscale.clients.docker
      +
      jumpscale.clients.gedis
      +
      +

      This module gives you all the facilities to communicate with gedis server …

      +
      jumpscale.clients.git
      +
      jumpscale.clients.github
      +
      +
      +
      +
      jumpscale.clients.gogs
      +
      +
      +
      +
      jumpscale.clients.kraken
      +
      +
      +
      +
      jumpscale.clients.liquid
      +
      +
      +
      +
      jumpscale.clients.mail
      +
      +
      +
      +
      jumpscale.clients.name
      +
      +
      +
      jumpscale.clients.redis
      +
      jumpscale.clients.s3
      +
      +
      +
      +
      jumpscale.clients.sendgrid
      +
      +
      +
      jumpscale.clients.sshclient
      @@ -50,10 +100,26 @@

      Sub-modules

      +
      jumpscale.clients.stellar
      +
      +
      +
      +
      jumpscale.clients.syncthing
      +
      +
      +
      +
      jumpscale.clients.taiga
      +
      +
      +
      jumpscale.clients.zdb
      +
      jumpscale.clients.zerotier
      +
      +
      +

    @@ -77,12 +143,28 @@

    Index

  • Sub-modules

  • @@ -92,4 +174,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/kraken/index.html b/docs/api/jumpscale/clients/kraken/index.html new file mode 100644 index 000000000..ff8fd7ebb --- /dev/null +++ b/docs/api/jumpscale/clients/kraken/index.html @@ -0,0 +1,101 @@ + + + + + + +jumpscale.clients.kraken API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.kraken

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .kraken import KrakenClient
    +
    +    return StoredFactory(KrakenClient)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.kraken.kraken
    +
    +

    Kraken client is a growing client support kraken.com API …

    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .kraken import KrakenClient
    +
    +    return StoredFactory(KrakenClient)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/kraken/kraken.html b/docs/api/jumpscale/clients/kraken/kraken.html new file mode 100644 index 000000000..ea12ad82a --- /dev/null +++ b/docs/api/jumpscale/clients/kraken/kraken.html @@ -0,0 +1,501 @@ + + + + + + +jumpscale.clients.kraken.kraken API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.kraken.kraken

    +
    +
    +

    Kraken client is a growing client support kraken.com API

    +

    Getting kraken clientn

    +

    Kraken client is available from j.clients.kraken

    +
    JS-NG> k1 = j.clients.kraken.get("k1")
    +
    +

    Getting a pair price

    +

    Here we try to get the value of XLMUSD (it's the default as well.)

    +
    JS-NG> p1 = k1.get_pair_price()                                                                                       
    +JS-NG> p1.last_trade                                                                                                  
    +'0.06943500'
    +
    +JS-NG> p1.bid                                                                                                         
    +'0.06930000'
    +
    +JS-NG> p1.ask                                                                                                         
    +'0.06943500'
    +
    +
    + +Expand source code + +
    """Kraken client is a growing client support kraken.com API
    +
    +## Getting kraken clientn
    +
    +Kraken client is available from `j.clients.kraken`
    +```
    +JS-NG> k1 = j.clients.kraken.get("k1")
    +```                                                                                
    +
    +## Getting a pair price
    +
    +Here we try to get the value of `XLMUSD` (it's the default as well.)
    +```
    +JS-NG> p1 = k1.get_pair_price()                                                                                       
    +JS-NG> p1.last_trade                                                                                                  
    +'0.06943500'
    +
    +JS-NG> p1.bid                                                                                                         
    +'0.06930000'
    +
    +JS-NG> p1.ask                                                                                                         
    +'0.06943500'
    +```
    +
    +
    +"""
    +
    +import requests
    +from jumpscale.loader import j
    +from jumpscale.clients.base import Client, Base
    +from jumpscale.core.base import fields
    +
    +
    +class Price(Base):
    +    pair = fields.String()
    +    ask = fields.String()
    +    bid = fields.String()
    +    last_trade = fields.String()
    +    stored_date = fields.DateTime()
    +
    +
    +class KrakenClient(Client):
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._session = requests.Session()
    +
    +    def _do_request(self, url, ex):
    +        res = self._session.get(url).json()
    +        if res["error"]:
    +            raise ex("\n".join(res["error"]))
    +        return res
    +
    +    def get_pair_price(self, pair="XLMUSD"):
    +        """Gets price of specified pair
    +
    +        Args:
    +            pair (str): pair name. Defaults to XLMUSD
    +
    +        Raises:
    +            j.exceptions.Input: If incorrect pair is provided
    +
    +        Returns:
    +            Price: Object containing ask, bid and last trade price
    +        """
    +        res = self._do_request(f"https://api.kraken.com/0/public/Ticker?pair={pair}", j.exceptions.Input)
    +        result = res["result"]
    +        key = list(result.keys())[0]
    +        data = {
    +            "pair": pair,
    +            "ask": result[key]["a"][0],
    +            "bid": result[key]["b"][0],
    +            "last_trade": result[key]["c"][0],
    +            "stored_date": j.data.time.utcnow().datetime,
    +        }
    +        return Price(**data)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class KrakenClient +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class KrakenClient(Client):
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._session = requests.Session()
    +
    +    def _do_request(self, url, ex):
    +        res = self._session.get(url).json()
    +        if res["error"]:
    +            raise ex("\n".join(res["error"]))
    +        return res
    +
    +    def get_pair_price(self, pair="XLMUSD"):
    +        """Gets price of specified pair
    +
    +        Args:
    +            pair (str): pair name. Defaults to XLMUSD
    +
    +        Raises:
    +            j.exceptions.Input: If incorrect pair is provided
    +
    +        Returns:
    +            Price: Object containing ask, bid and last trade price
    +        """
    +        res = self._do_request(f"https://api.kraken.com/0/public/Ticker?pair={pair}", j.exceptions.Input)
    +        result = res["result"]
    +        key = list(result.keys())[0]
    +        data = {
    +            "pair": pair,
    +            "ask": result[key]["a"][0],
    +            "bid": result[key]["b"][0],
    +            "last_trade": result[key]["c"][0],
    +            "stored_date": j.data.time.utcnow().datetime,
    +        }
    +        return Price(**data)
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def get_pair_price(self, pair='XLMUSD') +
    +
    +

    Gets price of specified pair

    +

    Args

    +
    +
    pair : str
    +
    pair name. Defaults to XLMUSD
    +
    +

    Raises

    +
    +
    j.exceptions.Input
    +
    If incorrect pair is provided
    +
    +

    Returns

    +
    +
    Price
    +
    Object containing ask, bid and last trade price
    +
    +
    + +Expand source code + +
    def get_pair_price(self, pair="XLMUSD"):
    +    """Gets price of specified pair
    +
    +    Args:
    +        pair (str): pair name. Defaults to XLMUSD
    +
    +    Raises:
    +        j.exceptions.Input: If incorrect pair is provided
    +
    +    Returns:
    +        Price: Object containing ask, bid and last trade price
    +    """
    +    res = self._do_request(f"https://api.kraken.com/0/public/Ticker?pair={pair}", j.exceptions.Input)
    +    result = res["result"]
    +    key = list(result.keys())[0]
    +    data = {
    +        "pair": pair,
    +        "ask": result[key]["a"][0],
    +        "bid": result[key]["b"][0],
    +        "last_trade": result[key]["c"][0],
    +        "stored_date": j.data.time.utcnow().datetime,
    +    }
    +    return Price(**data)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class Price +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class Price(Base):
    +    pair = fields.String()
    +    ask = fields.String()
    +    bid = fields.String()
    +    last_trade = fields.String()
    +    stored_date = fields.DateTime()
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var ask
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var bid
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var last_trade
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var pair
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var stored_date
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/liquid/index.html b/docs/api/jumpscale/clients/liquid/index.html new file mode 100644 index 000000000..2ecb632f6 --- /dev/null +++ b/docs/api/jumpscale/clients/liquid/index.html @@ -0,0 +1,101 @@ + + + + + + +jumpscale.clients.liquid API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.liquid

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .liquid import LiquidClient
    +
    +    return StoredFactory(LiquidClient)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.liquid.liquid
    +
    +

    Liquid client is a growing client support liquid.com API …

    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .liquid import LiquidClient
    +
    +    return StoredFactory(LiquidClient)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/liquid/liquid.html b/docs/api/jumpscale/clients/liquid/liquid.html new file mode 100644 index 000000000..dc1d136c4 --- /dev/null +++ b/docs/api/jumpscale/clients/liquid/liquid.html @@ -0,0 +1,1219 @@ + + + + + + +jumpscale.clients.liquid.liquid API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.liquid.liquid

    +
    +
    +

    Liquid client is a growing client support liquid.com API

    +

    Getting liquid client

    +

    Liquid client is available from j.clients.liquid ``` +JS-NG> 1 = j.clients.liquid.get("l1")

    +
    
    +## Getting a pair price
    +
    +Here we try to get the value of `TFTBTC` (it's the default as well.)
    +
    +

    JS-NG> p1 = k1.get_pair_price() +JS-NG> p1.last_trade +'0.06943500'

    +

    JS-NG> p1.bid +'0.06930000'

    +

    JS-NG> p1.ask +'0.06943500'

    +
    
    +## Orders
    +Here we handle orders on liquid network. The token_id and token_secret should be passed in the client creation
    +
    +

    client = j.clients.liquid.get("test_client",token_id=TOKEN_ID,token_secret=TOKEN_SECRET) +client.save()

    +

    JS-NG> client.create_order(order_type="limit",product_id=637,side="sell",quantity=12,price=50)

    +

    JS-NG> client.get_order(4238991143)

    +

    JS-NG> client.get_orders() +{'models': [{'id': 4238991143, 'order_type': 'limit', 'quantity': '12.0', 'disc_quantity': '0.0', 'iceberg_total_quantity': '0.0', 'side': 'sell', 'filled_quantity': '0.0', 'price': '50.0', 'created_at': 1617896032, 'updated_at': 1617896032, 'status': 'live', 'leverage_level': 1, 'source_exchange': 0, 'product_id': 637, 'margin_type': None, 'take_profit': None, 'stop_loss': None, 'trading_type': 'spot', 'product_code': 'CASH', 'funding_currency': 'BTC', 'crypto_account_id': None, 'currency_pair_code': 'TFTBTC', 'average_price': '0.0', 'target': 'spot', 'order_fee': '0.0', 'source_action': 'manual', 'unwound_trade_id': None, 'trade_id': None, 'client_order_id': None}], 'total_pages': 1, 'current_page': 1}

    +

    JS-NG> client.get_balance() +[{'currency': 'USD', 'balance': '0.0'}, {'currency': 'TFT', 'balance': '13.0'}, {'currency': 'XLM', 'balance': '0.0'}, {'currency': 'ETH', 'balance': '0.0'}, {'currency': 'BTC', 'balance': '0.0'}]

    +

    JS-NG> client.edit_order(order_id=4238991143,price=40)

    +

    JS-NG> client.cancel_order(4238991143) +{'id': 4238991143, 'order_type': 'limit', 'quantity': '12.0', 'disc_quantity': '0.0', 'iceberg_total_quantity': '0.0', 'side': 'sell', 'filled_quantity': '0.0', 'price': 40.0, 'created_at': 1617896032, 'updated_at': 1617897424, 'status': 'cancelled', 'leverage_level': 1, 'source_exchange': 'QUOINE', 'product_id': 637, 'margin_type': None, 'take_profit': None, 'stop_loss': None, 'trading_type': 'spot', 'product_code': 'CASH', 'funding_currency': 'BTC', 'crypto_account_id': None, 'currency_pair_code': 'TFTBTC', 'average_price': 0.0, 'target': 'spot', 'order_fee': 0.0, 'source_action': 'manual', 'unwound_trade_id': None, 'trade_id': None, 'client_order_id': None} +```

    +
    + +Expand source code + +
    """Liquid client is a growing client support liquid.com API
    +
    +## Getting liquid client
    +
    +Liquid client is available from `j.clients.liquid` ```
    +JS-NG> 1 = j.clients.liquid.get("l1")
    +```
    +
    +## Getting a pair price
    +
    +Here we try to get the value of `TFTBTC` (it's the default as well.)
    +```
    +JS-NG> p1 = k1.get_pair_price()
    +JS-NG> p1.last_trade
    +'0.06943500'
    +
    +JS-NG> p1.bid
    +'0.06930000'
    +
    +JS-NG> p1.ask
    +'0.06943500'
    +```
    +
    +## Orders
    +Here we handle orders on liquid network. The token_id and token_secret should be passed in the client creation
    +```
    +client = j.clients.liquid.get("test_client",token_id=TOKEN_ID,token_secret=TOKEN_SECRET)
    +client.save()
    +
    +JS-NG> client.create_order(order_type="limit",product_id=637,side="sell",quantity=12,price=50)
    +{'id': 4238991143, 'order_type': 'limit', 'quantity': '12.0', 'disc_quantity': '0.0', 'iceberg_total_quantity': '0.0', 'side': 'sell', 'filled_quantity': '0.0', 'price': 50.0, 'created_at': 1617896032, 'updated_at': 1617896032, 'status': 'live', 'leverage_level': 1, 'source_exchange': 'QUOINE', 'product_id': 637, 'margin_type': None, 'take_profit': None, 'stop_loss': None, 'trading_type': 'spot', 'product_code': 'CASH', 'funding_currency': 'BTC', 'crypto_account_id': None, 'currency_pair_code': 'TFTBTC', 'average_price': 0.0, 'target': 'spot', 'order_fee': 0.0, 'source_action': 'manual', 'unwound_trade_id': None, 'trade_id': None, 'client_order_id': None}
    +
    +JS-NG> client.get_order(4238991143)
    +{'id': 4238991143, 'order_type': 'limit', 'quantity': '12.0', 'disc_quantity': '0.0', 'iceberg_total_quantity': '0.0', 'side': 'sell', 'filled_quantity': '0.0', 'price': 50.0, 'created_at': 1617896032, 'updated_at': 1617896032, 'status': 'live', 'leverage_level': 1, 'source_exchange': 'QUOINE', 'product_id': 637, 'margin_type': None, 'take_profit': None, 'stop_loss': None, 'trading_type': 'spot', 'product_code': 'CASH', 'funding_currency': 'BTC', 'crypto_account_id': None, 'currency_pair_code': 'TFTBTC', 'average_price': 0.0, 'target': 'spot', 'order_fee': '0.0', 'source_action': 'manual', 'unwound_trade_id': None, 'trade_id': None, 'client_order_id': None, 'settings': None, 'trailing_stop_type': None, 'trailing_stop_value': None, 'executions': [], 'stop_triggered_time': None}
    +
    +JS-NG> client.get_orders()
    +{'models': [{'id': 4238991143, 'order_type': 'limit', 'quantity': '12.0', 'disc_quantity': '0.0', 'iceberg_total_quantity': '0.0', 'side': 'sell', 'filled_quantity': '0.0', 'price': '50.0', 'created_at': 1617896032, 'updated_at': 1617896032, 'status': 'live', 'leverage_level': 1, 'source_exchange': 0, 'product_id': 637, 'margin_type': None, 'take_profit': None, 'stop_loss': None, 'trading_type': 'spot', 'product_code': 'CASH', 'funding_currency': 'BTC', 'crypto_account_id': None, 'currency_pair_code': 'TFTBTC', 'average_price': '0.0', 'target': 'spot', 'order_fee': '0.0', 'source_action': 'manual', 'unwound_trade_id': None, 'trade_id': None, 'client_order_id': None}], 'total_pages': 1, 'current_page': 1}
    +
    +JS-NG> client.get_balance()
    +[{'currency': 'USD', 'balance': '0.0'}, {'currency': 'TFT', 'balance': '13.0'}, {'currency': 'XLM', 'balance': '0.0'}, {'currency': 'ETH', 'balance': '0.0'}, {'currency': 'BTC', 'balance': '0.0'}]
    +
    +JS-NG> client.edit_order(order_id=4238991143,price=40)
    +{'id': 4238991143, 'order_type': 'limit', 'quantity': '12.0', 'disc_quantity': '0.0', 'iceberg_total_quantity': '0.0', 'side': 'sell', 'filled_quantity': '0.0', 'price': 40.0, 'created_at': 1617896032, 'updated_at': 1617896342, 'status': 'live', 'leverage_level': 1, 'source_exchange': 'QUOINE', 'product_id': 637, 'margin_type': None, 'take_profit': None, 'stop_loss': None, 'trading_type': 'spot', 'product_code': 'CASH', 'funding_currency': 'BTC', 'crypto_account_id': None, 'currency_pair_code': 'TFTBTC', 'average_price': 0.0, 'target': 'spot', 'order_fee': 0.0, 'source_action': 'manual', 'unwound_trade_id': None, 'trade_id': None, 'client_order_id': None}
    +
    +JS-NG> client.cancel_order(4238991143)
    +{'id': 4238991143, 'order_type': 'limit', 'quantity': '12.0', 'disc_quantity': '0.0', 'iceberg_total_quantity': '0.0', 'side': 'sell', 'filled_quantity': '0.0', 'price': 40.0, 'created_at': 1617896032, 'updated_at': 1617897424, 'status': 'cancelled', 'leverage_level': 1, 'source_exchange': 'QUOINE', 'product_id': 637, 'margin_type': None, 'take_profit': None, 'stop_loss': None, 'trading_type': 'spot', 'product_code': 'CASH', 'funding_currency': 'BTC', 'crypto_account_id': None, 'currency_pair_code': 'TFTBTC', 'average_price': 0.0, 'target': 'spot', 'order_fee': 0.0, 'source_action': 'manual', 'unwound_trade_id': None, 'trade_id': None, 'client_order_id': None}
    +```
    +
    +"""
    +
    +import requests
    +import jwt
    +from jumpscale.loader import j
    +from jumpscale.clients.base import Client, Base
    +from jumpscale.core.base import fields
    +from urllib.parse import urlencode
    +
    +
    +class Price(Base):
    +    pair = fields.String()
    +    ask = fields.Float()
    +    bid = fields.Float()
    +
    +
    +class LiquidClient(Client):
    +    _url = fields.String(default="https://api.liquid.com/")
    +    _price = fields.Object(Price)
    +    token_id = fields.String(default=None)
    +    token_secret = fields.Secret(default=None)
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._session = requests.Session()
    +
    +    def _do_request(self, url, ex, headers=None, params=None, data=None, request_method="GET"):
    +        res = {}
    +        if headers:
    +            self._session.headers.update(headers)
    +        if request_method == "GET":
    +            res = self._session.get(url, headers=headers, params=params).json()
    +        elif request_method == "POST":
    +            res = self._session.post(url, headers=headers, json=data).json()
    +        elif request_method == "PUT":
    +            res = self._session.put(url, headers=headers, json=data).json()
    +
    +        return res
    +
    +    def get_pair_price(self, pair="TFTBTC"):
    +        """Gets price of specified pair
    +
    +        Args:
    +            pair (str): pair name. Defaults to TFTBTC
    +
    +        Raises:
    +            j.exceptions.Input: If incorrect pair is provided
    +
    +        Returns:
    +            Price: Object containing ask, bid and last trade price
    +        """
    +        res = self._do_request(f"{self._url}/products", j.exceptions.Input)
    +        result = [p for p in res if p["product_type"] == "CurrencyPair" and p["currency_pair_code"] == pair]
    +        if not result:
    +            raise j.exceptions.Input("no result")
    +        result = result[0]
    +        data = {"pair": pair, "ask": result["market_ask"], "bid": result["market_bid"]}
    +        s = Price(**data)
    +        return s
    +
    +    def do_authenticated_request(self, endpoint, data=None, params=None, request_method="GET"):
    +
    +        path = f"{self._url}{endpoint}"
    +        if params:
    +            encoded_query = urlencode(params)
    +        payload = {
    +            "path": f"{path}?{encoded_query}" if params else path,
    +            "nonce": j.data.time.get().timestamp * 1000,  # time in milliseconds
    +            "token_id": self.token_id,
    +        }
    +        signature = jwt.encode(payload, self.token_secret, "HS256")
    +        request_headers = {"X-Quoine-API-Version": "2", "X-Quoine-Auth": signature, "Content-Type": "application/json"}
    +        return self._do_request(
    +            path, j.exceptions.Input, headers=request_headers, params=params, data=data, request_method=request_method
    +        )
    +
    +    def create_order(
    +        self,
    +        order_type,
    +        side,
    +        quantity,
    +        product_id=637,
    +        price=None,
    +        trailing_stop_type=None,
    +        trailing_stop_value=None,
    +        trading_type=None,
    +        margin_type="cross",
    +        price_range=None,
    +        client_order_id=None,
    +        take_profit=None,
    +        stop_loss=None,
    +    ):
    +        """Create a new order
    +
    +        Args:
    +            order_type (str) : type of order [limit, market, market_with_range, trailing_stop, limit_post_only, stop],
    +            product_id (int) : type of product, e.g 637 (TFTBTC)
    +            side (str) : supported side of order [buy,sell],
    +            quantity (str) : quantity to buy or sell,
    +            price (str) : price per unit of cryptocurrency ,
    +            trailing_stop_type (str) : Only available if order_type is trailing_stop.  [price, percentage]
    +            trailing_stop_value (str) : Only available if order_type is trailing_stop.The distance the order should trail the market price.,
    +            trading_type=None (str) : Only available if leverage_level is greater than 1. [margin, cfd, perpetual]
    +            margin_type=None (str) : Only available if leverage_level is greater than 1. [cross, isolated],
    +            price_range=None (str) : Only available if order_type is market_with_range,
    +            client_order_id=None (str) :Custom unique identifying JSON string ,
    +            take_profit=None (str) : Only available if leverage_level is greater than 1. Take profit will be executed as a limit order,
    +            stop_loss=None (str) : Only available if leverage_level is greater than 1. Stop loss will be executed as a market order,
    +
    +        Raises:
    +            j.exceptions.Input: If invalid order type or side is provided.
    +
    +        Returns:
    +            Order response: dict
    +        """
    +        if order_type and order_type not in [
    +            "limit",
    +            "market",
    +            "market_with_range",
    +            "trailing_stop",
    +            "limit_post_only",
    +            "stop",
    +        ]:
    +            raise j.exceptions.Input("Invalid order type")
    +        if side and side not in ["buy", "sell"]:
    +            raise j.exceptions.Input("Invalid supported side. Value should be buy or sell")
    +
    +        data = {
    +            "order": {
    +                "order_type": order_type,
    +                "product_id": product_id,
    +                "side": side,
    +                "quantity": quantity,
    +                "price": price,
    +                "trailing_stop_type": trailing_stop_type,
    +                "trailing_stop_value": trailing_stop_value,
    +                "trading_type": trading_type,
    +                "margin_type": margin_type,
    +                "price_range": price_range,
    +                "client_order_id": client_order_id,
    +                "take_profit": take_profit,
    +                "stop_loss": stop_loss,
    +            }
    +        }
    +        return self.do_authenticated_request(endpoint="orders", request_method="POST", data=data)
    +
    +    def get_order(self, order_id):
    +        """Get an existing order
    +
    +        Args:
    +            order_id (str): id of order
    +        Returns:
    +            Order response: dict
    +        """
    +        endpoint = f"orders/{order_id}"
    +        return self.do_authenticated_request(endpoint=endpoint, request_method="GET")
    +
    +    def get_order_trades(self, order_id):
    +        """Get an existing order's trades
    +
    +        Args:
    +            order_id (str): id of order
    +        Returns:
    +            Order trades response: dict
    +        """
    +        endpoint = f"orders/{order_id}/trades"
    +        return self.do_authenticated_request(endpoint=endpoint, request_method="GET")
    +
    +    def get_orders(
    +        self,
    +        funding_currency=None,
    +        product_id=None,
    +        status=None,
    +        trading_type=None,
    +        with_details=None,
    +        limit=None,
    +        page=None,
    +    ):
    +        params = {
    +            "funding_currency": funding_currency,
    +            "product_id": product_id,
    +            "status": status,
    +            "trading_type": trading_type,
    +            "with_details": with_details,
    +            "limit": limit,
    +            "page": page,
    +        }
    +        # remove None paramas
    +        for key, item in params.copy().items():
    +            if not item:
    +                params.pop(key)
    +        return self.do_authenticated_request(endpoint="orders", params=params, request_method="GET")
    +
    +    def edit_order(
    +        self,
    +        order_id,
    +        order_type=None,
    +        product_id=637,
    +        side=None,
    +        quantity=None,
    +        price=None,
    +        trailing_stop_type=None,
    +        trailing_stop_value=None,
    +        trading_type=None,
    +        margin_type=None,
    +        price_range=None,
    +        client_order_id=None,
    +        take_profit=None,
    +        stop_loss=None,
    +    ):
    +        endpoint = f"orders/{order_id}"
    +        data = {
    +            "order": {
    +                "order_type": order_type,
    +                "product_id": product_id,
    +                "side": side,
    +                "quantity": quantity,
    +                "price": price,
    +                "trailing_stop_type": trailing_stop_type,
    +                "trailing_stop_value": trailing_stop_value,
    +                "trading_type": trading_type,
    +                "margin_type": margin_type,
    +                "price_range": price_range,
    +                "client_order_id": client_order_id,
    +                "take_profit": take_profit,
    +                "stop_loss": stop_loss,
    +            }
    +        }
    +
    +        return self.do_authenticated_request(endpoint=endpoint, request_method="PUT", data=data)
    +
    +    def cancel_order(self, order_id):
    +        endpoint = f"orders/{order_id}/cancel"
    +        return self.do_authenticated_request(endpoint=endpoint, request_method="PUT")
    +
    +    def get_balance(self):
    +        return self.do_authenticated_request(endpoint="accounts/balance", request_method="GET")
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class LiquidClient +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class LiquidClient(Client):
    +    _url = fields.String(default="https://api.liquid.com/")
    +    _price = fields.Object(Price)
    +    token_id = fields.String(default=None)
    +    token_secret = fields.Secret(default=None)
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._session = requests.Session()
    +
    +    def _do_request(self, url, ex, headers=None, params=None, data=None, request_method="GET"):
    +        res = {}
    +        if headers:
    +            self._session.headers.update(headers)
    +        if request_method == "GET":
    +            res = self._session.get(url, headers=headers, params=params).json()
    +        elif request_method == "POST":
    +            res = self._session.post(url, headers=headers, json=data).json()
    +        elif request_method == "PUT":
    +            res = self._session.put(url, headers=headers, json=data).json()
    +
    +        return res
    +
    +    def get_pair_price(self, pair="TFTBTC"):
    +        """Gets price of specified pair
    +
    +        Args:
    +            pair (str): pair name. Defaults to TFTBTC
    +
    +        Raises:
    +            j.exceptions.Input: If incorrect pair is provided
    +
    +        Returns:
    +            Price: Object containing ask, bid and last trade price
    +        """
    +        res = self._do_request(f"{self._url}/products", j.exceptions.Input)
    +        result = [p for p in res if p["product_type"] == "CurrencyPair" and p["currency_pair_code"] == pair]
    +        if not result:
    +            raise j.exceptions.Input("no result")
    +        result = result[0]
    +        data = {"pair": pair, "ask": result["market_ask"], "bid": result["market_bid"]}
    +        s = Price(**data)
    +        return s
    +
    +    def do_authenticated_request(self, endpoint, data=None, params=None, request_method="GET"):
    +
    +        path = f"{self._url}{endpoint}"
    +        if params:
    +            encoded_query = urlencode(params)
    +        payload = {
    +            "path": f"{path}?{encoded_query}" if params else path,
    +            "nonce": j.data.time.get().timestamp * 1000,  # time in milliseconds
    +            "token_id": self.token_id,
    +        }
    +        signature = jwt.encode(payload, self.token_secret, "HS256")
    +        request_headers = {"X-Quoine-API-Version": "2", "X-Quoine-Auth": signature, "Content-Type": "application/json"}
    +        return self._do_request(
    +            path, j.exceptions.Input, headers=request_headers, params=params, data=data, request_method=request_method
    +        )
    +
    +    def create_order(
    +        self,
    +        order_type,
    +        side,
    +        quantity,
    +        product_id=637,
    +        price=None,
    +        trailing_stop_type=None,
    +        trailing_stop_value=None,
    +        trading_type=None,
    +        margin_type="cross",
    +        price_range=None,
    +        client_order_id=None,
    +        take_profit=None,
    +        stop_loss=None,
    +    ):
    +        """Create a new order
    +
    +        Args:
    +            order_type (str) : type of order [limit, market, market_with_range, trailing_stop, limit_post_only, stop],
    +            product_id (int) : type of product, e.g 637 (TFTBTC)
    +            side (str) : supported side of order [buy,sell],
    +            quantity (str) : quantity to buy or sell,
    +            price (str) : price per unit of cryptocurrency ,
    +            trailing_stop_type (str) : Only available if order_type is trailing_stop.  [price, percentage]
    +            trailing_stop_value (str) : Only available if order_type is trailing_stop.The distance the order should trail the market price.,
    +            trading_type=None (str) : Only available if leverage_level is greater than 1. [margin, cfd, perpetual]
    +            margin_type=None (str) : Only available if leverage_level is greater than 1. [cross, isolated],
    +            price_range=None (str) : Only available if order_type is market_with_range,
    +            client_order_id=None (str) :Custom unique identifying JSON string ,
    +            take_profit=None (str) : Only available if leverage_level is greater than 1. Take profit will be executed as a limit order,
    +            stop_loss=None (str) : Only available if leverage_level is greater than 1. Stop loss will be executed as a market order,
    +
    +        Raises:
    +            j.exceptions.Input: If invalid order type or side is provided.
    +
    +        Returns:
    +            Order response: dict
    +        """
    +        if order_type and order_type not in [
    +            "limit",
    +            "market",
    +            "market_with_range",
    +            "trailing_stop",
    +            "limit_post_only",
    +            "stop",
    +        ]:
    +            raise j.exceptions.Input("Invalid order type")
    +        if side and side not in ["buy", "sell"]:
    +            raise j.exceptions.Input("Invalid supported side. Value should be buy or sell")
    +
    +        data = {
    +            "order": {
    +                "order_type": order_type,
    +                "product_id": product_id,
    +                "side": side,
    +                "quantity": quantity,
    +                "price": price,
    +                "trailing_stop_type": trailing_stop_type,
    +                "trailing_stop_value": trailing_stop_value,
    +                "trading_type": trading_type,
    +                "margin_type": margin_type,
    +                "price_range": price_range,
    +                "client_order_id": client_order_id,
    +                "take_profit": take_profit,
    +                "stop_loss": stop_loss,
    +            }
    +        }
    +        return self.do_authenticated_request(endpoint="orders", request_method="POST", data=data)
    +
    +    def get_order(self, order_id):
    +        """Get an existing order
    +
    +        Args:
    +            order_id (str): id of order
    +        Returns:
    +            Order response: dict
    +        """
    +        endpoint = f"orders/{order_id}"
    +        return self.do_authenticated_request(endpoint=endpoint, request_method="GET")
    +
    +    def get_order_trades(self, order_id):
    +        """Get an existing order's trades
    +
    +        Args:
    +            order_id (str): id of order
    +        Returns:
    +            Order trades response: dict
    +        """
    +        endpoint = f"orders/{order_id}/trades"
    +        return self.do_authenticated_request(endpoint=endpoint, request_method="GET")
    +
    +    def get_orders(
    +        self,
    +        funding_currency=None,
    +        product_id=None,
    +        status=None,
    +        trading_type=None,
    +        with_details=None,
    +        limit=None,
    +        page=None,
    +    ):
    +        params = {
    +            "funding_currency": funding_currency,
    +            "product_id": product_id,
    +            "status": status,
    +            "trading_type": trading_type,
    +            "with_details": with_details,
    +            "limit": limit,
    +            "page": page,
    +        }
    +        # remove None paramas
    +        for key, item in params.copy().items():
    +            if not item:
    +                params.pop(key)
    +        return self.do_authenticated_request(endpoint="orders", params=params, request_method="GET")
    +
    +    def edit_order(
    +        self,
    +        order_id,
    +        order_type=None,
    +        product_id=637,
    +        side=None,
    +        quantity=None,
    +        price=None,
    +        trailing_stop_type=None,
    +        trailing_stop_value=None,
    +        trading_type=None,
    +        margin_type=None,
    +        price_range=None,
    +        client_order_id=None,
    +        take_profit=None,
    +        stop_loss=None,
    +    ):
    +        endpoint = f"orders/{order_id}"
    +        data = {
    +            "order": {
    +                "order_type": order_type,
    +                "product_id": product_id,
    +                "side": side,
    +                "quantity": quantity,
    +                "price": price,
    +                "trailing_stop_type": trailing_stop_type,
    +                "trailing_stop_value": trailing_stop_value,
    +                "trading_type": trading_type,
    +                "margin_type": margin_type,
    +                "price_range": price_range,
    +                "client_order_id": client_order_id,
    +                "take_profit": take_profit,
    +                "stop_loss": stop_loss,
    +            }
    +        }
    +
    +        return self.do_authenticated_request(endpoint=endpoint, request_method="PUT", data=data)
    +
    +    def cancel_order(self, order_id):
    +        endpoint = f"orders/{order_id}/cancel"
    +        return self.do_authenticated_request(endpoint=endpoint, request_method="PUT")
    +
    +    def get_balance(self):
    +        return self.do_authenticated_request(endpoint="accounts/balance", request_method="GET")
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var token_id
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var token_secret
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def cancel_order(self, order_id) +
    +
    +
    +
    + +Expand source code + +
    def cancel_order(self, order_id):
    +    endpoint = f"orders/{order_id}/cancel"
    +    return self.do_authenticated_request(endpoint=endpoint, request_method="PUT")
    +
    +
    +
    +def create_order(self, order_type, side, quantity, product_id=637, price=None, trailing_stop_type=None, trailing_stop_value=None, trading_type=None, margin_type='cross', price_range=None, client_order_id=None, take_profit=None, stop_loss=None) +
    +
    +

    Create a new order

    +

    Args

    +

    order_type (str) : type of order [limit, market, market_with_range, trailing_stop, limit_post_only, stop], +product_id (int) : type of product, e.g 637 (TFTBTC) +side (str) : supported side of order [buy,sell], +quantity (str) : quantity to buy or sell, +price (str) : price per unit of cryptocurrency , +trailing_stop_type (str) : Only available if order_type is trailing_stop. +[price, percentage] +trailing_stop_value (str) : Only available if order_type is trailing_stop.The distance the order should trail the market price., +trading_type=None (str) : Only available if leverage_level is greater than 1. [margin, cfd, perpetual] +margin_type=None (str) : Only available if leverage_level is greater than 1. [cross, isolated], +price_range=None (str) : Only available if order_type is market_with_range, +client_order_id=None (str) :Custom unique identifying JSON string , +take_profit=None (str) : Only available if leverage_level is greater than 1. Take profit will be executed as a limit order, +stop_loss=None (str) : Only available if leverage_level is greater than 1. Stop loss will be executed as a market order,

    +

    Raises

    +
    +
    j.exceptions.Input
    +
    If invalid order type or side is provided.
    +
    +

    Returns

    +
    +
    Order response
    +
    dict
    +
    +
    + +Expand source code + +
    def create_order(
    +    self,
    +    order_type,
    +    side,
    +    quantity,
    +    product_id=637,
    +    price=None,
    +    trailing_stop_type=None,
    +    trailing_stop_value=None,
    +    trading_type=None,
    +    margin_type="cross",
    +    price_range=None,
    +    client_order_id=None,
    +    take_profit=None,
    +    stop_loss=None,
    +):
    +    """Create a new order
    +
    +    Args:
    +        order_type (str) : type of order [limit, market, market_with_range, trailing_stop, limit_post_only, stop],
    +        product_id (int) : type of product, e.g 637 (TFTBTC)
    +        side (str) : supported side of order [buy,sell],
    +        quantity (str) : quantity to buy or sell,
    +        price (str) : price per unit of cryptocurrency ,
    +        trailing_stop_type (str) : Only available if order_type is trailing_stop.  [price, percentage]
    +        trailing_stop_value (str) : Only available if order_type is trailing_stop.The distance the order should trail the market price.,
    +        trading_type=None (str) : Only available if leverage_level is greater than 1. [margin, cfd, perpetual]
    +        margin_type=None (str) : Only available if leverage_level is greater than 1. [cross, isolated],
    +        price_range=None (str) : Only available if order_type is market_with_range,
    +        client_order_id=None (str) :Custom unique identifying JSON string ,
    +        take_profit=None (str) : Only available if leverage_level is greater than 1. Take profit will be executed as a limit order,
    +        stop_loss=None (str) : Only available if leverage_level is greater than 1. Stop loss will be executed as a market order,
    +
    +    Raises:
    +        j.exceptions.Input: If invalid order type or side is provided.
    +
    +    Returns:
    +        Order response: dict
    +    """
    +    if order_type and order_type not in [
    +        "limit",
    +        "market",
    +        "market_with_range",
    +        "trailing_stop",
    +        "limit_post_only",
    +        "stop",
    +    ]:
    +        raise j.exceptions.Input("Invalid order type")
    +    if side and side not in ["buy", "sell"]:
    +        raise j.exceptions.Input("Invalid supported side. Value should be buy or sell")
    +
    +    data = {
    +        "order": {
    +            "order_type": order_type,
    +            "product_id": product_id,
    +            "side": side,
    +            "quantity": quantity,
    +            "price": price,
    +            "trailing_stop_type": trailing_stop_type,
    +            "trailing_stop_value": trailing_stop_value,
    +            "trading_type": trading_type,
    +            "margin_type": margin_type,
    +            "price_range": price_range,
    +            "client_order_id": client_order_id,
    +            "take_profit": take_profit,
    +            "stop_loss": stop_loss,
    +        }
    +    }
    +    return self.do_authenticated_request(endpoint="orders", request_method="POST", data=data)
    +
    +
    +
    +def do_authenticated_request(self, endpoint, data=None, params=None, request_method='GET') +
    +
    +
    +
    + +Expand source code + +
    def do_authenticated_request(self, endpoint, data=None, params=None, request_method="GET"):
    +
    +    path = f"{self._url}{endpoint}"
    +    if params:
    +        encoded_query = urlencode(params)
    +    payload = {
    +        "path": f"{path}?{encoded_query}" if params else path,
    +        "nonce": j.data.time.get().timestamp * 1000,  # time in milliseconds
    +        "token_id": self.token_id,
    +    }
    +    signature = jwt.encode(payload, self.token_secret, "HS256")
    +    request_headers = {"X-Quoine-API-Version": "2", "X-Quoine-Auth": signature, "Content-Type": "application/json"}
    +    return self._do_request(
    +        path, j.exceptions.Input, headers=request_headers, params=params, data=data, request_method=request_method
    +    )
    +
    +
    +
    +def edit_order(self, order_id, order_type=None, product_id=637, side=None, quantity=None, price=None, trailing_stop_type=None, trailing_stop_value=None, trading_type=None, margin_type=None, price_range=None, client_order_id=None, take_profit=None, stop_loss=None) +
    +
    +
    +
    + +Expand source code + +
    def edit_order(
    +    self,
    +    order_id,
    +    order_type=None,
    +    product_id=637,
    +    side=None,
    +    quantity=None,
    +    price=None,
    +    trailing_stop_type=None,
    +    trailing_stop_value=None,
    +    trading_type=None,
    +    margin_type=None,
    +    price_range=None,
    +    client_order_id=None,
    +    take_profit=None,
    +    stop_loss=None,
    +):
    +    endpoint = f"orders/{order_id}"
    +    data = {
    +        "order": {
    +            "order_type": order_type,
    +            "product_id": product_id,
    +            "side": side,
    +            "quantity": quantity,
    +            "price": price,
    +            "trailing_stop_type": trailing_stop_type,
    +            "trailing_stop_value": trailing_stop_value,
    +            "trading_type": trading_type,
    +            "margin_type": margin_type,
    +            "price_range": price_range,
    +            "client_order_id": client_order_id,
    +            "take_profit": take_profit,
    +            "stop_loss": stop_loss,
    +        }
    +    }
    +
    +    return self.do_authenticated_request(endpoint=endpoint, request_method="PUT", data=data)
    +
    +
    +
    +def get_balance(self) +
    +
    +
    +
    + +Expand source code + +
    def get_balance(self):
    +    return self.do_authenticated_request(endpoint="accounts/balance", request_method="GET")
    +
    +
    +
    +def get_order(self, order_id) +
    +
    +

    Get an existing order

    +

    Args

    +
    +
    order_id : str
    +
    id of order
    +
    +

    Returns

    +
    +
    Order response
    +
    dict
    +
    +
    + +Expand source code + +
    def get_order(self, order_id):
    +    """Get an existing order
    +
    +    Args:
    +        order_id (str): id of order
    +    Returns:
    +        Order response: dict
    +    """
    +    endpoint = f"orders/{order_id}"
    +    return self.do_authenticated_request(endpoint=endpoint, request_method="GET")
    +
    +
    +
    +def get_order_trades(self, order_id) +
    +
    +

    Get an existing order's trades

    +

    Args

    +
    +
    order_id : str
    +
    id of order
    +
    +

    Returns

    +
    +
    Order trades response
    +
    dict
    +
    +
    + +Expand source code + +
    def get_order_trades(self, order_id):
    +    """Get an existing order's trades
    +
    +    Args:
    +        order_id (str): id of order
    +    Returns:
    +        Order trades response: dict
    +    """
    +    endpoint = f"orders/{order_id}/trades"
    +    return self.do_authenticated_request(endpoint=endpoint, request_method="GET")
    +
    +
    +
    +def get_orders(self, funding_currency=None, product_id=None, status=None, trading_type=None, with_details=None, limit=None, page=None) +
    +
    +
    +
    + +Expand source code + +
    def get_orders(
    +    self,
    +    funding_currency=None,
    +    product_id=None,
    +    status=None,
    +    trading_type=None,
    +    with_details=None,
    +    limit=None,
    +    page=None,
    +):
    +    params = {
    +        "funding_currency": funding_currency,
    +        "product_id": product_id,
    +        "status": status,
    +        "trading_type": trading_type,
    +        "with_details": with_details,
    +        "limit": limit,
    +        "page": page,
    +    }
    +    # remove None paramas
    +    for key, item in params.copy().items():
    +        if not item:
    +            params.pop(key)
    +    return self.do_authenticated_request(endpoint="orders", params=params, request_method="GET")
    +
    +
    +
    +def get_pair_price(self, pair='TFTBTC') +
    +
    +

    Gets price of specified pair

    +

    Args

    +
    +
    pair : str
    +
    pair name. Defaults to TFTBTC
    +
    +

    Raises

    +
    +
    j.exceptions.Input
    +
    If incorrect pair is provided
    +
    +

    Returns

    +
    +
    Price
    +
    Object containing ask, bid and last trade price
    +
    +
    + +Expand source code + +
    def get_pair_price(self, pair="TFTBTC"):
    +    """Gets price of specified pair
    +
    +    Args:
    +        pair (str): pair name. Defaults to TFTBTC
    +
    +    Raises:
    +        j.exceptions.Input: If incorrect pair is provided
    +
    +    Returns:
    +        Price: Object containing ask, bid and last trade price
    +    """
    +    res = self._do_request(f"{self._url}/products", j.exceptions.Input)
    +    result = [p for p in res if p["product_type"] == "CurrencyPair" and p["currency_pair_code"] == pair]
    +    if not result:
    +        raise j.exceptions.Input("no result")
    +    result = result[0]
    +    data = {"pair": pair, "ask": result["market_ask"], "bid": result["market_bid"]}
    +    s = Price(**data)
    +    return s
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class Price +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class Price(Base):
    +    pair = fields.String()
    +    ask = fields.Float()
    +    bid = fields.Float()
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var ask
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var bid
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var pair
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/mail/index.html b/docs/api/jumpscale/clients/mail/index.html new file mode 100644 index 000000000..b29fff417 --- /dev/null +++ b/docs/api/jumpscale/clients/mail/index.html @@ -0,0 +1,101 @@ + + + + + + +jumpscale.clients.mail API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.mail

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .mail import MailClient
    +
    +    return StoredFactory(MailClient)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.mail.mail
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .mail import MailClient
    +
    +    return StoredFactory(MailClient)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/mail/mail.html b/docs/api/jumpscale/clients/mail/mail.html new file mode 100644 index 000000000..517f84630 --- /dev/null +++ b/docs/api/jumpscale/clients/mail/mail.html @@ -0,0 +1,591 @@ + + + + + + +jumpscale.clients.mail.mail API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.mail.mail

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +import smtplib
    +import mimetypes
    +from email import encoders
    +from email.mime.audio import MIMEAudio
    +from email.mime.base import MIMEBase
    +from email.mime.image import MIMEImage
    +from email.mime.multipart import MIMEMultipart
    +from email.mime.text import MIMEText
    +
    +from jumpscale.clients.base import Client
    +from jumpscale.core.base import fields
    +
    +
    +class MailClient(Client):
    +    name = fields.String()
    +    smtp_server = fields.String()
    +    smtp_port = fields.Integer()
    +    login = fields.String()
    +    password = fields.String()
    +    sender_email = fields.String()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +    @property
    +    def is_ssl(self):
    +        return self.smtp_port in [465, 587]
    +
    +    def send(self, recipients, sender="", subject="", message="", files=None, mimetype=None):
    +        """ Send an email to the recipients from the sender containing the message required and any attached files given by the paths in files
    +        :param recipients: Recipients of the message
    +        :type recipients: mixed, str or list
    +        :param sender: Sender of the email
    +        :type sender: str
    +        :param subject: Subject of the email
    +        :type subject: str
    +        :param message: Body of the email
    +        :type message: str
    +        :param files: List of paths to files to attach
    +        :type files: list of strings
    +        :param mimetype: Type of the body plain, html or None for autodetection
    +        :type mimetype: str
    +        """
    +        if not sender:
    +            sender = self.sender_email
    +        if isinstance(recipients, str):
    +            recipients = [recipients]
    +        server = smtplib.SMTP(self.smtp_server, self.smtp_port)
    +        server.ehlo()
    +        if self.is_ssl:
    +            server.starttls()
    +        if self.login:
    +            server.login(self.login, self.password)
    +
    +        if mimetype is None:
    +            if "<html>" in message:
    +                mimetype = "html"
    +            else:
    +                mimetype = "plain"
    +
    +        msg = MIMEText(message, mimetype)
    +
    +        msg["Subject"] = subject
    +        msg["From"] = sender
    +        msg["To"] = ",".join(recipients)
    +
    +        if files:
    +            txtmsg = msg
    +            msg = MIMEMultipart()
    +            msg["Subject"] = subject
    +            msg["From"] = sender
    +            msg["To"] = ",".join(recipients)
    +            msg.attach(txtmsg)
    +            for fl in files:
    +                # Guess the content type based on the file's extension.  Encoding
    +                # will be ignored, although we should check for simple things like
    +                # gzip'd or compressed files.
    +                filename = j.sals.fs.basename(fl)
    +                ctype, encoding = mimetypes.guess_type(fl)
    +                content = j.sals.fs.read_file(fl)
    +                if ctype is None or encoding is not None:
    +                    # No guess could be made, or the file is encoded (compressed), so
    +                    # use a generic bag-of-bits type.
    +                    ctype = "application/octet-stream"
    +                maintype, subtype = ctype.split("/", 1)
    +                if maintype == "text":
    +                    attachement = MIMEText(content, _subtype=subtype)
    +                elif maintype == "image":
    +                    attachement = MIMEImage(content, _subtype=subtype)
    +                elif maintype == "audio":
    +                    attachement = MIMEAudio(content, _subtype=subtype)
    +                else:
    +                    attachement = MIMEBase(maintype, subtype)
    +                    attachement.set_payload(content)
    +                    # Encode the payload using Base64
    +                    encoders.encode_base64(attachement)
    +                # Set the filename parameter
    +                attachement.add_header("Content-Disposition", "attachment", filename=filename)
    +                msg.attach(attachement)
    +        server.sendmail(sender, recipients, msg.as_string())
    +        server.close()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class MailClient +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class MailClient(Client):
    +    name = fields.String()
    +    smtp_server = fields.String()
    +    smtp_port = fields.Integer()
    +    login = fields.String()
    +    password = fields.String()
    +    sender_email = fields.String()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +    @property
    +    def is_ssl(self):
    +        return self.smtp_port in [465, 587]
    +
    +    def send(self, recipients, sender="", subject="", message="", files=None, mimetype=None):
    +        """ Send an email to the recipients from the sender containing the message required and any attached files given by the paths in files
    +        :param recipients: Recipients of the message
    +        :type recipients: mixed, str or list
    +        :param sender: Sender of the email
    +        :type sender: str
    +        :param subject: Subject of the email
    +        :type subject: str
    +        :param message: Body of the email
    +        :type message: str
    +        :param files: List of paths to files to attach
    +        :type files: list of strings
    +        :param mimetype: Type of the body plain, html or None for autodetection
    +        :type mimetype: str
    +        """
    +        if not sender:
    +            sender = self.sender_email
    +        if isinstance(recipients, str):
    +            recipients = [recipients]
    +        server = smtplib.SMTP(self.smtp_server, self.smtp_port)
    +        server.ehlo()
    +        if self.is_ssl:
    +            server.starttls()
    +        if self.login:
    +            server.login(self.login, self.password)
    +
    +        if mimetype is None:
    +            if "<html>" in message:
    +                mimetype = "html"
    +            else:
    +                mimetype = "plain"
    +
    +        msg = MIMEText(message, mimetype)
    +
    +        msg["Subject"] = subject
    +        msg["From"] = sender
    +        msg["To"] = ",".join(recipients)
    +
    +        if files:
    +            txtmsg = msg
    +            msg = MIMEMultipart()
    +            msg["Subject"] = subject
    +            msg["From"] = sender
    +            msg["To"] = ",".join(recipients)
    +            msg.attach(txtmsg)
    +            for fl in files:
    +                # Guess the content type based on the file's extension.  Encoding
    +                # will be ignored, although we should check for simple things like
    +                # gzip'd or compressed files.
    +                filename = j.sals.fs.basename(fl)
    +                ctype, encoding = mimetypes.guess_type(fl)
    +                content = j.sals.fs.read_file(fl)
    +                if ctype is None or encoding is not None:
    +                    # No guess could be made, or the file is encoded (compressed), so
    +                    # use a generic bag-of-bits type.
    +                    ctype = "application/octet-stream"
    +                maintype, subtype = ctype.split("/", 1)
    +                if maintype == "text":
    +                    attachement = MIMEText(content, _subtype=subtype)
    +                elif maintype == "image":
    +                    attachement = MIMEImage(content, _subtype=subtype)
    +                elif maintype == "audio":
    +                    attachement = MIMEAudio(content, _subtype=subtype)
    +                else:
    +                    attachement = MIMEBase(maintype, subtype)
    +                    attachement.set_payload(content)
    +                    # Encode the payload using Base64
    +                    encoders.encode_base64(attachement)
    +                # Set the filename parameter
    +                attachement.add_header("Content-Disposition", "attachment", filename=filename)
    +                msg.attach(attachement)
    +        server.sendmail(sender, recipients, msg.as_string())
    +        server.close()
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var is_ssl
    +
    +
    +
    + +Expand source code + +
    @property
    +def is_ssl(self):
    +    return self.smtp_port in [465, 587]
    +
    +
    +
    var login
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var name
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var password
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var sender_email
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var smtp_port
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var smtp_server
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def send(self, recipients, sender='', subject='', message='', files=None, mimetype=None) +
    +
    +

    Send an email to the recipients from the sender containing the message required and any attached files given by the paths in files +:param recipients: Recipients of the message +:type recipients: mixed, str or list +:param sender: Sender of the email +:type sender: str +:param subject: Subject of the email +:type subject: str +:param message: Body of the email +:type message: str +:param files: List of paths to files to attach +:type files: list of strings +:param mimetype: Type of the body plain, html or None for autodetection +:type mimetype: str

    +
    + +Expand source code + +
    def send(self, recipients, sender="", subject="", message="", files=None, mimetype=None):
    +    """ Send an email to the recipients from the sender containing the message required and any attached files given by the paths in files
    +    :param recipients: Recipients of the message
    +    :type recipients: mixed, str or list
    +    :param sender: Sender of the email
    +    :type sender: str
    +    :param subject: Subject of the email
    +    :type subject: str
    +    :param message: Body of the email
    +    :type message: str
    +    :param files: List of paths to files to attach
    +    :type files: list of strings
    +    :param mimetype: Type of the body plain, html or None for autodetection
    +    :type mimetype: str
    +    """
    +    if not sender:
    +        sender = self.sender_email
    +    if isinstance(recipients, str):
    +        recipients = [recipients]
    +    server = smtplib.SMTP(self.smtp_server, self.smtp_port)
    +    server.ehlo()
    +    if self.is_ssl:
    +        server.starttls()
    +    if self.login:
    +        server.login(self.login, self.password)
    +
    +    if mimetype is None:
    +        if "<html>" in message:
    +            mimetype = "html"
    +        else:
    +            mimetype = "plain"
    +
    +    msg = MIMEText(message, mimetype)
    +
    +    msg["Subject"] = subject
    +    msg["From"] = sender
    +    msg["To"] = ",".join(recipients)
    +
    +    if files:
    +        txtmsg = msg
    +        msg = MIMEMultipart()
    +        msg["Subject"] = subject
    +        msg["From"] = sender
    +        msg["To"] = ",".join(recipients)
    +        msg.attach(txtmsg)
    +        for fl in files:
    +            # Guess the content type based on the file's extension.  Encoding
    +            # will be ignored, although we should check for simple things like
    +            # gzip'd or compressed files.
    +            filename = j.sals.fs.basename(fl)
    +            ctype, encoding = mimetypes.guess_type(fl)
    +            content = j.sals.fs.read_file(fl)
    +            if ctype is None or encoding is not None:
    +                # No guess could be made, or the file is encoded (compressed), so
    +                # use a generic bag-of-bits type.
    +                ctype = "application/octet-stream"
    +            maintype, subtype = ctype.split("/", 1)
    +            if maintype == "text":
    +                attachement = MIMEText(content, _subtype=subtype)
    +            elif maintype == "image":
    +                attachement = MIMEImage(content, _subtype=subtype)
    +            elif maintype == "audio":
    +                attachement = MIMEAudio(content, _subtype=subtype)
    +            else:
    +                attachement = MIMEBase(maintype, subtype)
    +                attachement.set_payload(content)
    +                # Encode the payload using Base64
    +                encoders.encode_base64(attachement)
    +            # Set the filename parameter
    +            attachement.add_header("Content-Disposition", "attachment", filename=filename)
    +            msg.attach(attachement)
    +    server.sendmail(sender, recipients, msg.as_string())
    +    server.close()
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/name/index.html b/docs/api/jumpscale/clients/name/index.html new file mode 100644 index 000000000..40d0925fc --- /dev/null +++ b/docs/api/jumpscale/clients/name/index.html @@ -0,0 +1,101 @@ + + + + + + +jumpscale.clients.name API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.name

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .name import NameClient
    +
    +    return StoredFactory(NameClient)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.name.name
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .name import NameClient
    +
    +    return StoredFactory(NameClient)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/name/name.html b/docs/api/jumpscale/clients/name/name.html new file mode 100644 index 000000000..9bb3bbfaa --- /dev/null +++ b/docs/api/jumpscale/clients/name/name.html @@ -0,0 +1,268 @@ + + + + + + +jumpscale.clients.name.name API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.name.name

    +
    +
    +
    + +Expand source code + +
    from jumpscale.clients.base import Client
    +from jumpscale.core.base import fields
    +from namecom import Name
    +from jumpscale.core import events
    +from jumpscale.core.base.events import AttributeUpdateEvent
    +
    +
    +class NameClientAttributeUpdated(AttributeUpdateEvent):
    +    pass
    +
    +
    +class NameClient(Client):
    +    username = fields.String()
    +    token = fields.Secret()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.__client = None
    +
    +    def _attr_updated(self, name, value):
    +        super()._attr_updated(name, value)
    +        # this will allow other people to listen to this event too
    +        event = NameClientAttributeUpdated(self, name, value)
    +        events.notify(event)
    +
    +        # reset client
    +        self.__client = None
    +
    +    @property
    +    def nameclient(self):
    +        if not self.__client:
    +            self.__client = Name(self.username, self.token)
    +        return self.__client
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class NameClient +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class NameClient(Client):
    +    username = fields.String()
    +    token = fields.Secret()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.__client = None
    +
    +    def _attr_updated(self, name, value):
    +        super()._attr_updated(name, value)
    +        # this will allow other people to listen to this event too
    +        event = NameClientAttributeUpdated(self, name, value)
    +        events.notify(event)
    +
    +        # reset client
    +        self.__client = None
    +
    +    @property
    +    def nameclient(self):
    +        if not self.__client:
    +            self.__client = Name(self.username, self.token)
    +        return self.__client
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var nameclient
    +
    +
    +
    + +Expand source code + +
    @property
    +def nameclient(self):
    +    if not self.__client:
    +        self.__client = Name(self.username, self.token)
    +    return self.__client
    +
    +
    +
    var token
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var username
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class NameClientAttributeUpdated +(instance, name, new_value) +
    +
    +
    +
    + +Expand source code + +
    class NameClientAttributeUpdated(AttributeUpdateEvent):
    +    pass
    +
    +

    Ancestors

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/redis/index.html b/docs/api/jumpscale/clients/redis/index.html index ae61da0f8..7a8aa4f5c 100644 --- a/docs/api/jumpscale/clients/redis/index.html +++ b/docs/api/jumpscale/clients/redis/index.html @@ -98,4 +98,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/redis/redis.html b/docs/api/jumpscale/clients/redis/redis.html index 58979f0ef..20e3acc34 100644 --- a/docs/api/jumpscale/clients/redis/redis.html +++ b/docs/api/jumpscale/clients/redis/redis.html @@ -74,7 +74,7 @@

    Module jumpscale.clients.redis.redis

    self.__client = Redis(self.hostname, self.port) return self.__client - + def is_running(self): try: return self.redis_client.ping() @@ -154,7 +154,7 @@

    Args

    self.__client = Redis(self.hostname, self.port) return self.__client - + def is_running(self): try: return self.redis_client.ping() @@ -360,4 +360,4 @@

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/s3/index.html b/docs/api/jumpscale/clients/s3/index.html new file mode 100644 index 000000000..cd2aa2f3c --- /dev/null +++ b/docs/api/jumpscale/clients/s3/index.html @@ -0,0 +1,99 @@ + + + + + + +jumpscale.clients.s3 API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.s3

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .s3 import S3Client
    +
    +    return StoredFactory(S3Client)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.s3.s3
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .s3 import S3Client
    +
    +    return StoredFactory(S3Client)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/s3/s3.html b/docs/api/jumpscale/clients/s3/s3.html new file mode 100644 index 000000000..4f0d5d086 --- /dev/null +++ b/docs/api/jumpscale/clients/s3/s3.html @@ -0,0 +1,743 @@ + + + + + + +jumpscale.clients.s3.s3 API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.s3.s3

    +
    +
    +
    + +Expand source code + +
    from jumpscale.clients.base import Client
    +from jumpscale.core.base import Base, fields
    +import urllib3
    +import certifi
    +
    +from minio import Minio
    +from minio.error import ResponseError, BucketAlreadyOwnedByYou, BucketAlreadyExists
    +
    +
    +class S3Client(Client):
    +    name = fields.String()
    +    address = fields.String()
    +    port = fields.Integer()
    +    access_key = fields.String()
    +    secret_key = fields.String()
    +    bucket = fields.String()
    +    create_bucket = fields.Boolean()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        # Create the http client to be able to set timeout
    +        http_client = urllib3.PoolManager(
    +            timeout=5,
    +            cert_reqs="CERT_REQUIRED",
    +            ca_certs=certifi.where(),
    +            retries=urllib3.Retry(total=3, backoff_factor=0.2, status_forcelist=[500, 502, 503, 504]),
    +        )
    +        # Create Minio client
    +        self.client = Minio(
    +            "{}:{}".format(self.address, self.port),
    +            access_key=self.access_key,
    +            secret_key=self.secret_key,
    +            secure=False,
    +            http_client=http_client,
    +        )
    +
    +        if self.create_bucket:
    +            self._bucket_create(self.bucket)
    +
    +    def _bucket_create(self, name):
    +        try:
    +            self.client.make_bucket(name, location="us-east-1")
    +        except BucketAlreadyOwnedByYou as err:
    +            pass
    +        except BucketAlreadyExists as err:
    +            pass
    +        except ResponseError as err:
    +            raise
    +
    +    def upload(self, bucket_name, object_name, file_path, content_type="text/plain", meta_data=None):
    +        """Upload contents from a file specified by file_path, to object_name
    +
    +        :param bucket_name: name of bucket
    +        :type bucket_name: str
    +        :param object_name: name of object
    +        :type object_name: str
    +        :param file_path: local path from which object data will be read
    +        :type file_path: str
    +        :param content_type: content type of the object, defaults to 'text/plain'
    +        :type content_type: str, optional
    +        :param meta_data: additional metadata, defaults to None
    +        :type meta_data: dict, optional
    +        :raises ValueError: if file given by file_path is not found
    +        :return: str
    +        :rtype: Object etag computed by the minio server.
    +        """
    +        if not j.sals.fs.exists(file_path):
    +            raise j.exceptions.Value("file: {} not found".format(file_path))
    +        return self.client.fput_object(bucket_name, object_name, file_path, content_type, meta_data)
    +
    +    def download(self, bucket_name, object_name, file_path):
    +        """Download and save the object as a file in the local filesystem
    +
    +        :param bucket_name: name of bucket
    +        :type bucket_name: str
    +        :param object_name: name of object
    +        :type object_name: str
    +        :param file_path: local path to which object data will be written
    +        :type file_path: str
    +        :return: object stat info (includes: size, etag, content_type,last_modified, metadata)
    +        :rtype: Object
    +        """
    +
    +        return self.client.fget_object(bucket_name, object_name, file_path)
    +
    +    def list_buckets(self):
    +        """List all buckets
    +
    +        :return: bucketList, bucket.name, bucket.creation_date
    +        :rtype: function, str, date
    +        """
    +        return self.client.list_buckets()
    +
    +    def list_objects(self, bucket_name, prefix=None, recursive=None):
    +        """List objects in a specific bucket
    +
    +        :param bucket_name: name of bucket
    +        :type bucket_name: str
    +        :param prefix: prefix of the objects that should be listed, defaults to None
    +        :type prefix: str, optional
    +        :param recursive: True indicates recursive style listing and False indicates directory style listing delimited by '/', defaults to None
    +        :type recursive: bool, optional
    +        :return: Iterator for all the objects in the bucket (includes: bucket_name, object_name,is_dir, size, etag, last_modified)
    +        :rtype: Object
    +        """
    +
    +        return self.client.list_objects(bucket_name, prefix=prefix, recursive=recursive)
    +
    +    def remove_bucket(self, bucket_name):
    +        """Remove a bucket.
    +
    +        :param bucket_name: name of bucket to be removed
    +        :type bucket_name: str
    +        """
    +        return self.client.remove_bucket(bucket_name)
    +
    +    def remove_object(self, bucket_name, object_name):
    +        """Remove object from bucket
    +
    +        :param bucket_name: name of bucket
    +        :type bucket_name: str
    +        :param object_name: name of object to be removed
    +        :type object_name: str
    +        """
    +
    +        return self.client.remove_object(bucket_name, object_name)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class S3Client +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class S3Client(Client):
    +    name = fields.String()
    +    address = fields.String()
    +    port = fields.Integer()
    +    access_key = fields.String()
    +    secret_key = fields.String()
    +    bucket = fields.String()
    +    create_bucket = fields.Boolean()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        # Create the http client to be able to set timeout
    +        http_client = urllib3.PoolManager(
    +            timeout=5,
    +            cert_reqs="CERT_REQUIRED",
    +            ca_certs=certifi.where(),
    +            retries=urllib3.Retry(total=3, backoff_factor=0.2, status_forcelist=[500, 502, 503, 504]),
    +        )
    +        # Create Minio client
    +        self.client = Minio(
    +            "{}:{}".format(self.address, self.port),
    +            access_key=self.access_key,
    +            secret_key=self.secret_key,
    +            secure=False,
    +            http_client=http_client,
    +        )
    +
    +        if self.create_bucket:
    +            self._bucket_create(self.bucket)
    +
    +    def _bucket_create(self, name):
    +        try:
    +            self.client.make_bucket(name, location="us-east-1")
    +        except BucketAlreadyOwnedByYou as err:
    +            pass
    +        except BucketAlreadyExists as err:
    +            pass
    +        except ResponseError as err:
    +            raise
    +
    +    def upload(self, bucket_name, object_name, file_path, content_type="text/plain", meta_data=None):
    +        """Upload contents from a file specified by file_path, to object_name
    +
    +        :param bucket_name: name of bucket
    +        :type bucket_name: str
    +        :param object_name: name of object
    +        :type object_name: str
    +        :param file_path: local path from which object data will be read
    +        :type file_path: str
    +        :param content_type: content type of the object, defaults to 'text/plain'
    +        :type content_type: str, optional
    +        :param meta_data: additional metadata, defaults to None
    +        :type meta_data: dict, optional
    +        :raises ValueError: if file given by file_path is not found
    +        :return: str
    +        :rtype: Object etag computed by the minio server.
    +        """
    +        if not j.sals.fs.exists(file_path):
    +            raise j.exceptions.Value("file: {} not found".format(file_path))
    +        return self.client.fput_object(bucket_name, object_name, file_path, content_type, meta_data)
    +
    +    def download(self, bucket_name, object_name, file_path):
    +        """Download and save the object as a file in the local filesystem
    +
    +        :param bucket_name: name of bucket
    +        :type bucket_name: str
    +        :param object_name: name of object
    +        :type object_name: str
    +        :param file_path: local path to which object data will be written
    +        :type file_path: str
    +        :return: object stat info (includes: size, etag, content_type,last_modified, metadata)
    +        :rtype: Object
    +        """
    +
    +        return self.client.fget_object(bucket_name, object_name, file_path)
    +
    +    def list_buckets(self):
    +        """List all buckets
    +
    +        :return: bucketList, bucket.name, bucket.creation_date
    +        :rtype: function, str, date
    +        """
    +        return self.client.list_buckets()
    +
    +    def list_objects(self, bucket_name, prefix=None, recursive=None):
    +        """List objects in a specific bucket
    +
    +        :param bucket_name: name of bucket
    +        :type bucket_name: str
    +        :param prefix: prefix of the objects that should be listed, defaults to None
    +        :type prefix: str, optional
    +        :param recursive: True indicates recursive style listing and False indicates directory style listing delimited by '/', defaults to None
    +        :type recursive: bool, optional
    +        :return: Iterator for all the objects in the bucket (includes: bucket_name, object_name,is_dir, size, etag, last_modified)
    +        :rtype: Object
    +        """
    +
    +        return self.client.list_objects(bucket_name, prefix=prefix, recursive=recursive)
    +
    +    def remove_bucket(self, bucket_name):
    +        """Remove a bucket.
    +
    +        :param bucket_name: name of bucket to be removed
    +        :type bucket_name: str
    +        """
    +        return self.client.remove_bucket(bucket_name)
    +
    +    def remove_object(self, bucket_name, object_name):
    +        """Remove object from bucket
    +
    +        :param bucket_name: name of bucket
    +        :type bucket_name: str
    +        :param object_name: name of object to be removed
    +        :type object_name: str
    +        """
    +
    +        return self.client.remove_object(bucket_name, object_name)
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var access_key
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var address
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var bucket
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var create_bucket
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var name
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var port
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var secret_key
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def download(self, bucket_name, object_name, file_path) +
    +
    +

    Download and save the object as a file in the local filesystem

    +

    :param bucket_name: name of bucket +:type bucket_name: str +:param object_name: name of object +:type object_name: str +:param file_path: local path to which object data will be written +:type file_path: str +:return: object stat info (includes: size, etag, content_type,last_modified, metadata) +:rtype: Object

    +
    + +Expand source code + +
    def download(self, bucket_name, object_name, file_path):
    +    """Download and save the object as a file in the local filesystem
    +
    +    :param bucket_name: name of bucket
    +    :type bucket_name: str
    +    :param object_name: name of object
    +    :type object_name: str
    +    :param file_path: local path to which object data will be written
    +    :type file_path: str
    +    :return: object stat info (includes: size, etag, content_type,last_modified, metadata)
    +    :rtype: Object
    +    """
    +
    +    return self.client.fget_object(bucket_name, object_name, file_path)
    +
    +
    +
    +def list_buckets(self) +
    +
    +

    List all buckets

    +

    :return: bucketList, bucket.name, bucket.creation_date +:rtype: function, str, date

    +
    + +Expand source code + +
    def list_buckets(self):
    +    """List all buckets
    +
    +    :return: bucketList, bucket.name, bucket.creation_date
    +    :rtype: function, str, date
    +    """
    +    return self.client.list_buckets()
    +
    +
    +
    +def list_objects(self, bucket_name, prefix=None, recursive=None) +
    +
    +

    List objects in a specific bucket

    +

    :param bucket_name: name of bucket +:type bucket_name: str +:param prefix: prefix of the objects that should be listed, defaults to None +:type prefix: str, optional +:param recursive: True indicates recursive style listing and False indicates directory style listing delimited by '/', defaults to None +:type recursive: bool, optional +:return: Iterator for all the objects in the bucket (includes: bucket_name, object_name,is_dir, size, etag, last_modified) +:rtype: Object

    +
    + +Expand source code + +
    def list_objects(self, bucket_name, prefix=None, recursive=None):
    +    """List objects in a specific bucket
    +
    +    :param bucket_name: name of bucket
    +    :type bucket_name: str
    +    :param prefix: prefix of the objects that should be listed, defaults to None
    +    :type prefix: str, optional
    +    :param recursive: True indicates recursive style listing and False indicates directory style listing delimited by '/', defaults to None
    +    :type recursive: bool, optional
    +    :return: Iterator for all the objects in the bucket (includes: bucket_name, object_name,is_dir, size, etag, last_modified)
    +    :rtype: Object
    +    """
    +
    +    return self.client.list_objects(bucket_name, prefix=prefix, recursive=recursive)
    +
    +
    +
    +def remove_bucket(self, bucket_name) +
    +
    +

    Remove a bucket.

    +

    :param bucket_name: name of bucket to be removed +:type bucket_name: str

    +
    + +Expand source code + +
    def remove_bucket(self, bucket_name):
    +    """Remove a bucket.
    +
    +    :param bucket_name: name of bucket to be removed
    +    :type bucket_name: str
    +    """
    +    return self.client.remove_bucket(bucket_name)
    +
    +
    +
    +def remove_object(self, bucket_name, object_name) +
    +
    +

    Remove object from bucket

    +

    :param bucket_name: name of bucket +:type bucket_name: str +:param object_name: name of object to be removed +:type object_name: str

    +
    + +Expand source code + +
    def remove_object(self, bucket_name, object_name):
    +    """Remove object from bucket
    +
    +    :param bucket_name: name of bucket
    +    :type bucket_name: str
    +    :param object_name: name of object to be removed
    +    :type object_name: str
    +    """
    +
    +    return self.client.remove_object(bucket_name, object_name)
    +
    +
    +
    +def upload(self, bucket_name, object_name, file_path, content_type='text/plain', meta_data=None) +
    +
    +

    Upload contents from a file specified by file_path, to object_name

    +

    :param bucket_name: name of bucket +:type bucket_name: str +:param object_name: name of object +:type object_name: str +:param file_path: local path from which object data will be read +:type file_path: str +:param content_type: content type of the object, defaults to 'text/plain' +:type content_type: str, optional +:param meta_data: additional metadata, defaults to None +:type meta_data: dict, optional +:raises ValueError: if file given by file_path is not found +:return: str +:rtype: Object etag computed by the minio server.

    +
    + +Expand source code + +
    def upload(self, bucket_name, object_name, file_path, content_type="text/plain", meta_data=None):
    +    """Upload contents from a file specified by file_path, to object_name
    +
    +    :param bucket_name: name of bucket
    +    :type bucket_name: str
    +    :param object_name: name of object
    +    :type object_name: str
    +    :param file_path: local path from which object data will be read
    +    :type file_path: str
    +    :param content_type: content type of the object, defaults to 'text/plain'
    +    :type content_type: str, optional
    +    :param meta_data: additional metadata, defaults to None
    +    :type meta_data: dict, optional
    +    :raises ValueError: if file given by file_path is not found
    +    :return: str
    +    :rtype: Object etag computed by the minio server.
    +    """
    +    if not j.sals.fs.exists(file_path):
    +        raise j.exceptions.Value("file: {} not found".format(file_path))
    +    return self.client.fput_object(bucket_name, object_name, file_path, content_type, meta_data)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/sendgrid/index.html b/docs/api/jumpscale/clients/sendgrid/index.html new file mode 100644 index 000000000..809c85bdb --- /dev/null +++ b/docs/api/jumpscale/clients/sendgrid/index.html @@ -0,0 +1,99 @@ + + + + + + +jumpscale.clients.sendgrid API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.sendgrid

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .sendgrid import SendGridClient
    +
    +    return StoredFactory(SendGridClient)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.sendgrid.sendgrid
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .sendgrid import SendGridClient
    +
    +    return StoredFactory(SendGridClient)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/sendgrid/sendgrid.html b/docs/api/jumpscale/clients/sendgrid/sendgrid.html new file mode 100644 index 000000000..f929875ae --- /dev/null +++ b/docs/api/jumpscale/clients/sendgrid/sendgrid.html @@ -0,0 +1,302 @@ + + + + + + +jumpscale.clients.sendgrid.sendgrid API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.sendgrid.sendgrid

    +
    +
    +
    + +Expand source code + +
    import base64
    +import os
    +import io
    +import sendgrid
    +from sendgrid.helpers.mail import Mail, Attachment
    +from python_http_client.exceptions import HTTPError
    +from jumpscale.loader import j
    +from jumpscale.clients.base import Client
    +from jumpscale.core.base import fields
    +
    +
    +class SendGridClient(Client):
    +    apikey = fields.String()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +    def build_attachment(self, filepath, typ="application/pdf"):
    +        """
    +        Returns a valid sendgrid attachment from typical attachment object.
    +        """
    +        data = io.BytesIO()
    +        with open(filepath, "rb") as f:
    +            while True:
    +                d = f.read(2 ** 20)
    +                if not d:
    +                    break
    +                data.write(d)
    +        data.seek(0)
    +        attachment = sendgrid.Attachment()
    +        attachment.file_content = j.data.serializers.base64.encode(data.read()).decode()
    +        attachment.file_type = typ
    +        attachment.file_name = os.path.basename(filepath)
    +        attachment.disposition = "attachment"
    +        return attachment
    +
    +    def send(self, sender, subject, html_content="<strong>Email</strong>", recipients=None, attachments=None):
    +        recipients = recipients or []
    +        attachments = attachments or []
    +        recipients = list(set(recipients))
    +        mail = Mail(from_email=sender, to_emails=recipients, subject=subject, html_content=html_content)
    +        for at in attachments:
    +            mail.add_attachment(at)
    +        try:
    +            sg = sendgrid.SendGridAPIClient(self.apikey)
    +            # response=sg.send(mail)
    +            response = sg.client.mail.send.post(request_body=mail.get())
    +            print(response.status_code)
    +            print(response.body)
    +            print(response.headers)
    +        except HTTPError as e:
    +            raise e
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SendGridClient +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class SendGridClient(Client):
    +    apikey = fields.String()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +    def build_attachment(self, filepath, typ="application/pdf"):
    +        """
    +        Returns a valid sendgrid attachment from typical attachment object.
    +        """
    +        data = io.BytesIO()
    +        with open(filepath, "rb") as f:
    +            while True:
    +                d = f.read(2 ** 20)
    +                if not d:
    +                    break
    +                data.write(d)
    +        data.seek(0)
    +        attachment = sendgrid.Attachment()
    +        attachment.file_content = j.data.serializers.base64.encode(data.read()).decode()
    +        attachment.file_type = typ
    +        attachment.file_name = os.path.basename(filepath)
    +        attachment.disposition = "attachment"
    +        return attachment
    +
    +    def send(self, sender, subject, html_content="<strong>Email</strong>", recipients=None, attachments=None):
    +        recipients = recipients or []
    +        attachments = attachments or []
    +        recipients = list(set(recipients))
    +        mail = Mail(from_email=sender, to_emails=recipients, subject=subject, html_content=html_content)
    +        for at in attachments:
    +            mail.add_attachment(at)
    +        try:
    +            sg = sendgrid.SendGridAPIClient(self.apikey)
    +            # response=sg.send(mail)
    +            response = sg.client.mail.send.post(request_body=mail.get())
    +            print(response.status_code)
    +            print(response.body)
    +            print(response.headers)
    +        except HTTPError as e:
    +            raise e
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var apikey
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def build_attachment(self, filepath, typ='application/pdf') +
    +
    +

    Returns a valid sendgrid attachment from typical attachment object.

    +
    + +Expand source code + +
    def build_attachment(self, filepath, typ="application/pdf"):
    +    """
    +    Returns a valid sendgrid attachment from typical attachment object.
    +    """
    +    data = io.BytesIO()
    +    with open(filepath, "rb") as f:
    +        while True:
    +            d = f.read(2 ** 20)
    +            if not d:
    +                break
    +            data.write(d)
    +    data.seek(0)
    +    attachment = sendgrid.Attachment()
    +    attachment.file_content = j.data.serializers.base64.encode(data.read()).decode()
    +    attachment.file_type = typ
    +    attachment.file_name = os.path.basename(filepath)
    +    attachment.disposition = "attachment"
    +    return attachment
    +
    +
    +
    +def send(self, sender, subject, html_content='<strong>Email</strong>', recipients=None, attachments=None) +
    +
    +
    +
    + +Expand source code + +
    def send(self, sender, subject, html_content="<strong>Email</strong>", recipients=None, attachments=None):
    +    recipients = recipients or []
    +    attachments = attachments or []
    +    recipients = list(set(recipients))
    +    mail = Mail(from_email=sender, to_emails=recipients, subject=subject, html_content=html_content)
    +    for at in attachments:
    +        mail.add_attachment(at)
    +    try:
    +        sg = sendgrid.SendGridAPIClient(self.apikey)
    +        # response=sg.send(mail)
    +        response = sg.client.mail.send.post(request_body=mail.get())
    +        print(response.status_code)
    +        print(response.body)
    +        print(response.headers)
    +    except HTTPError as e:
    +        raise e
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/sshclient/index.html b/docs/api/jumpscale/clients/sshclient/index.html index 901137305..4aac5f501 100644 --- a/docs/api/jumpscale/clients/sshclient/index.html +++ b/docs/api/jumpscale/clients/sshclient/index.html @@ -100,4 +100,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/sshclient/sshclient.html b/docs/api/jumpscale/clients/sshclient/sshclient.html index 39b083386..3e5c81f42 100644 --- a/docs/api/jumpscale/clients/sshclient/sshclient.html +++ b/docs/api/jumpscale/clients/sshclient/sshclient.html @@ -595,4 +595,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/sshkey/index.html b/docs/api/jumpscale/clients/sshkey/index.html index e6f247a79..f8483d87d 100644 --- a/docs/api/jumpscale/clients/sshkey/index.html +++ b/docs/api/jumpscale/clients/sshkey/index.html @@ -99,4 +99,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/sshkey/sshkey.html b/docs/api/jumpscale/clients/sshkey/sshkey.html index 44ccdacfe..f15385dc2 100644 --- a/docs/api/jumpscale/clients/sshkey/sshkey.html +++ b/docs/api/jumpscale/clients/sshkey/sshkey.html @@ -620,4 +620,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/stellar/balance.html b/docs/api/jumpscale/clients/stellar/balance.html new file mode 100644 index 000000000..2e80d81ce --- /dev/null +++ b/docs/api/jumpscale/clients/stellar/balance.html @@ -0,0 +1,489 @@ + + + + + + +jumpscale.clients.stellar.balance API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.stellar.balance

    +
    +
    +
    + +Expand source code + +
    from stellar_sdk import TransactionEnvelope
    +import datetime
    +import time
    +
    +
    +class Balance:
    +    def __init__(self, balance=0.0, asset_code="XLM", asset_issuer=None):
    +        self.balance = balance
    +        self.asset_code = asset_code
    +        self.asset_issuer = asset_issuer
    +
    +    @staticmethod
    +    def from_horizon_response(response_balance):
    +        balance = response_balance["balance"]
    +        if response_balance["asset_type"] == "native":
    +            asset_code = "XLM"
    +            asset_issuer = None
    +        elif response_balance["asset_type"] == "liquidity_pool_shares":
    +            return None
    +        else:
    +            asset_code = response_balance["asset_code"]
    +            asset_issuer = response_balance["asset_issuer"]
    +        return Balance(balance, asset_code, asset_issuer)
    +
    +    def is_native(self):
    +        return self.asset_code == "XLM" and self.asset_issuer is None
    +
    +    def __str__(self):
    +        representation = f"{self.balance} {self.asset_code}"
    +        if self.asset_issuer is not None:
    +            representation += f":{self.asset_issuer}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +class EscrowAccount:
    +    def __init__(self, address, unlockhashes, balances, network_passphrase, _get_unlockhash_transaction):
    +        self.address = address
    +        self.unlockhashes = unlockhashes
    +        self.balances = balances
    +        self.network_passphrase = network_passphrase
    +        self._get_unlockhash_transaction = _get_unlockhash_transaction
    +        self.unlock_time = None
    +        self._set_unlock_conditions()
    +
    +    def _set_unlock_conditions(self):
    +        for unlockhash in self.unlockhashes:
    +            unlockhash_tx = self._get_unlockhash_transaction(unlockhash=unlockhash)
    +            if unlockhash_tx is None:
    +                return
    +
    +            txe = TransactionEnvelope.from_xdr(unlockhash_tx["transaction_xdr"], self.network_passphrase)
    +            tx = txe.transaction
    +            if tx.preconditions is not None:
    +                if tx.preconditions.time_bounds is not None:
    +                    self.unlock_time = tx.time_bounds.min_time
    +
    +    def can_be_unlocked(self):
    +        if len(self.unlockhashes) == 0:
    +            return True
    +        if self.unlock_time is not None:
    +            return time.time() > self.unlock_time
    +        return False
    +
    +    def __str__(self):
    +        if self.unlock_time is not None:
    +            representation = "Locked until {unlock_time:%B %d %Y %H:%M:%S} on escrow account {account_id} ".format(
    +                account_id=self.address, unlock_time=datetime.datetime.fromtimestamp(self.unlock_time)
    +            )
    +        else:
    +            if len(self.unlockhashes) == 0:
    +                representation = f"Free to be claimed on escrow account {self.address}"
    +            else:
    +                representation = f"Escrow account {self.address} with unknown unlockhashes {self.unlockhashes}"
    +        for balance in self.balances:
    +            representation += f"\n- {balance.balance} {balance.asset_code}"
    +            if balance.asset_issuer is not None:
    +                representation += f":{balance.asset_issuer}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +class VestingAccount:
    +    def __init__(self, address, balances, scheme):
    +        self.address = address
    +        self.balances = balances
    +        self.scheme = scheme
    +
    +    def __str__(self):
    +        representation = f"Vesting Account {self.address}"
    +        for balance in self.balances:
    +            representation += f"\n- {balance.balance} {balance.asset_code}"
    +            if balance.asset_issuer is not None:
    +                representation += f":{balance.asset_issuer}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +class AccountBalances:
    +    def __init__(self, address):
    +        self.address = address
    +        self.balances = []
    +        self.escrow_accounts = []
    +        self.vesting_accounts = []
    +
    +    def add_balance(self, balance):
    +        self.balances.append(balance)
    +
    +    def add_escrow_account(self, account):
    +        if type(account) is VestingAccount:
    +            self.vesting_accounts.append(account)
    +        else:
    +            self.escrow_accounts.append(account)
    +
    +    def __str__(self):
    +        representation = "Balances"
    +        for balance in self.balances:
    +            representation += "\n  " + str(balance)
    +        for vesting_account in self.vesting_accounts:
    +            representation += f"\n{str(vesting_account)}"
    +
    +        if self.escrow_accounts:
    +            representation += "\nLocked balances:"
    +            for escrow_account in self.escrow_accounts:
    +                representation += f"\n - {str(escrow_account)}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class AccountBalances +(address) +
    +
    +
    +
    + +Expand source code + +
    class AccountBalances:
    +    def __init__(self, address):
    +        self.address = address
    +        self.balances = []
    +        self.escrow_accounts = []
    +        self.vesting_accounts = []
    +
    +    def add_balance(self, balance):
    +        self.balances.append(balance)
    +
    +    def add_escrow_account(self, account):
    +        if type(account) is VestingAccount:
    +            self.vesting_accounts.append(account)
    +        else:
    +            self.escrow_accounts.append(account)
    +
    +    def __str__(self):
    +        representation = "Balances"
    +        for balance in self.balances:
    +            representation += "\n  " + str(balance)
    +        for vesting_account in self.vesting_accounts:
    +            representation += f"\n{str(vesting_account)}"
    +
    +        if self.escrow_accounts:
    +            representation += "\nLocked balances:"
    +            for escrow_account in self.escrow_accounts:
    +                representation += f"\n - {str(escrow_account)}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +

    Methods

    +
    +
    +def add_balance(self, balance) +
    +
    +
    +
    + +Expand source code + +
    def add_balance(self, balance):
    +    self.balances.append(balance)
    +
    +
    +
    +def add_escrow_account(self, account) +
    +
    +
    +
    + +Expand source code + +
    def add_escrow_account(self, account):
    +    if type(account) is VestingAccount:
    +        self.vesting_accounts.append(account)
    +    else:
    +        self.escrow_accounts.append(account)
    +
    +
    +
    +
    +
    +class Balance +(balance=0.0, asset_code='XLM', asset_issuer=None) +
    +
    +
    +
    + +Expand source code + +
    class Balance:
    +    def __init__(self, balance=0.0, asset_code="XLM", asset_issuer=None):
    +        self.balance = balance
    +        self.asset_code = asset_code
    +        self.asset_issuer = asset_issuer
    +
    +    @staticmethod
    +    def from_horizon_response(response_balance):
    +        balance = response_balance["balance"]
    +        if response_balance["asset_type"] == "native":
    +            asset_code = "XLM"
    +            asset_issuer = None
    +        elif response_balance["asset_type"] == "liquidity_pool_shares":
    +            return None
    +        else:
    +            asset_code = response_balance["asset_code"]
    +            asset_issuer = response_balance["asset_issuer"]
    +        return Balance(balance, asset_code, asset_issuer)
    +
    +    def is_native(self):
    +        return self.asset_code == "XLM" and self.asset_issuer is None
    +
    +    def __str__(self):
    +        representation = f"{self.balance} {self.asset_code}"
    +        if self.asset_issuer is not None:
    +            representation += f":{self.asset_issuer}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +

    Static methods

    +
    +
    +def from_horizon_response(response_balance) +
    +
    +
    +
    + +Expand source code + +
    @staticmethod
    +def from_horizon_response(response_balance):
    +    balance = response_balance["balance"]
    +    if response_balance["asset_type"] == "native":
    +        asset_code = "XLM"
    +        asset_issuer = None
    +    elif response_balance["asset_type"] == "liquidity_pool_shares":
    +        return None
    +    else:
    +        asset_code = response_balance["asset_code"]
    +        asset_issuer = response_balance["asset_issuer"]
    +    return Balance(balance, asset_code, asset_issuer)
    +
    +
    +
    +

    Methods

    +
    +
    +def is_native(self) +
    +
    +
    +
    + +Expand source code + +
    def is_native(self):
    +    return self.asset_code == "XLM" and self.asset_issuer is None
    +
    +
    +
    +
    +
    +class EscrowAccount +(address, unlockhashes, balances, network_passphrase, _get_unlockhash_transaction) +
    +
    +
    +
    + +Expand source code + +
    class EscrowAccount:
    +    def __init__(self, address, unlockhashes, balances, network_passphrase, _get_unlockhash_transaction):
    +        self.address = address
    +        self.unlockhashes = unlockhashes
    +        self.balances = balances
    +        self.network_passphrase = network_passphrase
    +        self._get_unlockhash_transaction = _get_unlockhash_transaction
    +        self.unlock_time = None
    +        self._set_unlock_conditions()
    +
    +    def _set_unlock_conditions(self):
    +        for unlockhash in self.unlockhashes:
    +            unlockhash_tx = self._get_unlockhash_transaction(unlockhash=unlockhash)
    +            if unlockhash_tx is None:
    +                return
    +
    +            txe = TransactionEnvelope.from_xdr(unlockhash_tx["transaction_xdr"], self.network_passphrase)
    +            tx = txe.transaction
    +            if tx.preconditions is not None:
    +                if tx.preconditions.time_bounds is not None:
    +                    self.unlock_time = tx.time_bounds.min_time
    +
    +    def can_be_unlocked(self):
    +        if len(self.unlockhashes) == 0:
    +            return True
    +        if self.unlock_time is not None:
    +            return time.time() > self.unlock_time
    +        return False
    +
    +    def __str__(self):
    +        if self.unlock_time is not None:
    +            representation = "Locked until {unlock_time:%B %d %Y %H:%M:%S} on escrow account {account_id} ".format(
    +                account_id=self.address, unlock_time=datetime.datetime.fromtimestamp(self.unlock_time)
    +            )
    +        else:
    +            if len(self.unlockhashes) == 0:
    +                representation = f"Free to be claimed on escrow account {self.address}"
    +            else:
    +                representation = f"Escrow account {self.address} with unknown unlockhashes {self.unlockhashes}"
    +        for balance in self.balances:
    +            representation += f"\n- {balance.balance} {balance.asset_code}"
    +            if balance.asset_issuer is not None:
    +                representation += f":{balance.asset_issuer}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +

    Methods

    +
    +
    +def can_be_unlocked(self) +
    +
    +
    +
    + +Expand source code + +
    def can_be_unlocked(self):
    +    if len(self.unlockhashes) == 0:
    +        return True
    +    if self.unlock_time is not None:
    +        return time.time() > self.unlock_time
    +    return False
    +
    +
    +
    +
    +
    +class VestingAccount +(address, balances, scheme) +
    +
    +
    +
    + +Expand source code + +
    class VestingAccount:
    +    def __init__(self, address, balances, scheme):
    +        self.address = address
    +        self.balances = balances
    +        self.scheme = scheme
    +
    +    def __str__(self):
    +        representation = f"Vesting Account {self.address}"
    +        for balance in self.balances:
    +            representation += f"\n- {balance.balance} {balance.asset_code}"
    +            if balance.asset_issuer is not None:
    +                representation += f":{balance.asset_issuer}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/stellar/exceptions.html b/docs/api/jumpscale/clients/stellar/exceptions.html new file mode 100644 index 000000000..080ee4153 --- /dev/null +++ b/docs/api/jumpscale/clients/stellar/exceptions.html @@ -0,0 +1,207 @@ + + + + + + +jumpscale.clients.stellar.exceptions API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.stellar.exceptions

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +
    +
    +class TemporaryProblem(j.exceptions.JSException):
    +    def __init__(self, message):
    +        self.message = message
    +        super().__init__(message)
    +
    +    def __str__(self):
    +        return self.message
    +
    +
    +class NoTrustLine(j.exceptions.JSException):
    +    def __init__(self):
    +        super().__init__("Receiver has no trustline")
    +
    +    def __str__(self):
    +        return "Receiver has no trustline"
    +
    +
    +class UnAuthorized(j.exceptions.JSException):
    +    def __init__(self, transaction_xdr):
    +        self.transaction_xdr = transaction_xdr
    +        super().__init__("Unauthorized or not enough signatures")
    +
    +    def __str__(self):
    +        return f"Unauthorized or not enough signatures for transaction envelope {self.transaction_xdr}"
    +
    +
    +class TooLate(TemporaryProblem):
    +    def __init__(self):
    +        super().__init__("The transaction failed to be submitted to the network in time")
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class NoTrustLine +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class NoTrustLine(j.exceptions.JSException):
    +    def __init__(self):
    +        super().__init__("Receiver has no trustline")
    +
    +    def __str__(self):
    +        return "Receiver has no trustline"
    +
    +

    Ancestors

    +
      +
    • JSException
    • +
    • builtins.Exception
    • +
    • builtins.BaseException
    • +
    +
    +
    +class TemporaryProblem +(message) +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class TemporaryProblem(j.exceptions.JSException):
    +    def __init__(self, message):
    +        self.message = message
    +        super().__init__(message)
    +
    +    def __str__(self):
    +        return self.message
    +
    +

    Ancestors

    +
      +
    • JSException
    • +
    • builtins.Exception
    • +
    • builtins.BaseException
    • +
    +

    Subclasses

    + +
    +
    +class TooLate +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class TooLate(TemporaryProblem):
    +    def __init__(self):
    +        super().__init__("The transaction failed to be submitted to the network in time")
    +
    +

    Ancestors

    + +
    +
    +class UnAuthorized +(transaction_xdr) +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class UnAuthorized(j.exceptions.JSException):
    +    def __init__(self, transaction_xdr):
    +        self.transaction_xdr = transaction_xdr
    +        super().__init__("Unauthorized or not enough signatures")
    +
    +    def __str__(self):
    +        return f"Unauthorized or not enough signatures for transaction envelope {self.transaction_xdr}"
    +
    +

    Ancestors

    +
      +
    • JSException
    • +
    • builtins.Exception
    • +
    • builtins.BaseException
    • +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/stellar/index.html b/docs/api/jumpscale/clients/stellar/index.html new file mode 100644 index 000000000..abe902556 --- /dev/null +++ b/docs/api/jumpscale/clients/stellar/index.html @@ -0,0 +1,437 @@ + + + + + + +jumpscale.clients.stellar API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.stellar

    +
    +
    +
    + +Expand source code + +
    import requests
    +
    +from jumpscale.loader import j
    +from jumpscale.core.base import StoredFactory
    +from stellar_sdk import Keypair
    +from .exceptions import *
    +from requests.exceptions import RequestException
    +
    +TRANSACTION_FEES = 0.01
    +
    +THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES = {
    +    "TEST": "https://testnet.threefold.io/threefoldfoundation/transactionfunding_service/fund_transaction",
    +    "STD": "https://tokenservices.threefold.io/threefoldfoundation/transactionfunding_service/fund_transaction",
    +}
    +
    +HORIZON_NETWORKS = {
    +    "TEST": "https://horizon-testnet.stellar.org",
    +    "STD": "https://horizon.stellar.org",
    +}
    +
    +NETWORKS = HORIZON_NETWORKS.keys()
    +
    +
    +class StellarFactory(StoredFactory):
    +    def new(self, name, secret=None, *args, **kwargs):
    +        instance = super().new(name, *args, **kwargs)
    +
    +        if not secret:
    +            key_pair = Keypair.random()
    +            instance.secret = key_pair.secret
    +        else:
    +            instance.secret = secret
    +
    +        return instance
    +
    +    def check_stellar_service(self, networks=NETWORKS):
    +        """This method will check if stellar and token service is up or not"""
    +        for newtwork in networks:
    +            stellar_url = HORIZON_NETWORKS[newtwork]
    +            tokenservices_url = THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[newtwork]
    +
    +            # check stellar service
    +            try:
    +                j.tools.http.get(stellar_url)
    +            except RequestException:
    +                return False
    +
    +            # check token services
    +            try:
    +                j.tools.http.options(tokenservices_url)
    +            except RequestException:
    +                return False
    +
    +        return True
    +
    +    def create_testnet_funded_wallet(self, name: str) -> bool:
    +        """This method will create a testnet wallet and fund it from the facuet
    +
    +        Args:
    +            name (str): name of the wallet
    +
    +        """
    +        wallet = self.new(name)
    +        try:
    +            wallet.network = "TEST"
    +            wallet.activate_through_friendbot()
    +            wallet.add_known_trustline("TFT")
    +            wallet.save()
    +        except Exception as e:
    +            self.delete(name)
    +            raise j.exceptions.Runtime(f"Failed to create the wallet due to token service")
    +
    +        headers = {"Content-Type": "application/json"}
    +        url = "https://gettft.testnet.grid.tf/tft_faucet/api/transfer"
    +        data = {"destination": wallet.address}
    +
    +        try:
    +            response = requests.post(url, json=data, headers=headers)
    +        except requests.exceptions.HTTPError:
    +            self.delete(name)
    +            raise j.exceptions.Runtime(
    +                f"Failed to fund wallet can't reach {url} due to connection error. Changes will be reverted."
    +            )
    +
    +        if response.status_code != 200:
    +            self.delete(name)
    +            raise j.exceptions.Runtime(
    +                f"Failed to fund the wallet due to to facuet server error. Changes will be reverted."
    +            )
    +
    +        j.logger.info("Wallet created successfully")
    +        return True
    +
    +
    +def export_module_as():
    +
    +    from .stellar import Stellar
    +
    +    return StellarFactory(Stellar)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.stellar.balance
    +
    +
    +
    +
    jumpscale.clients.stellar.exceptions
    +
    +
    +
    +
    jumpscale.clients.stellar.stellar
    +
    +
    +
    +
    jumpscale.clients.stellar.transaction
    +
    +
    +
    +
    jumpscale.clients.stellar.vesting
    +
    +
    +
    +
    jumpscale.clients.stellar.wrapped
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +
    +    from .stellar import Stellar
    +
    +    return StellarFactory(Stellar)
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class StellarFactory +(type_, name_=None, parent_instance_=None, parent_factory_=None) +
    +
    +

    Stored factories are a custom type of Factory, which uses current configured store backend +to store all instance configurations.

    +

    get a new stored factory given the type to create and store instances for.

    +

    Any factory can have a name, parent Base instance and a parent factory.

    +

    Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

    +

    Args

    +
    +
    type_ : Base
    +
    Base class type
    +
    name_ : str, optional
    +
    factory name. Defaults to None.
    +
    parent_instance_ : Base, optional
    +
    a parent Base instance. Defaults to None.
    +
    parent_factory_ : Factory, optional
    +
    a parent Factory. Defaults to None.
    +
    +
    + +Expand source code + +
    class StellarFactory(StoredFactory):
    +    def new(self, name, secret=None, *args, **kwargs):
    +        instance = super().new(name, *args, **kwargs)
    +
    +        if not secret:
    +            key_pair = Keypair.random()
    +            instance.secret = key_pair.secret
    +        else:
    +            instance.secret = secret
    +
    +        return instance
    +
    +    def check_stellar_service(self, networks=NETWORKS):
    +        """This method will check if stellar and token service is up or not"""
    +        for newtwork in networks:
    +            stellar_url = HORIZON_NETWORKS[newtwork]
    +            tokenservices_url = THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[newtwork]
    +
    +            # check stellar service
    +            try:
    +                j.tools.http.get(stellar_url)
    +            except RequestException:
    +                return False
    +
    +            # check token services
    +            try:
    +                j.tools.http.options(tokenservices_url)
    +            except RequestException:
    +                return False
    +
    +        return True
    +
    +    def create_testnet_funded_wallet(self, name: str) -> bool:
    +        """This method will create a testnet wallet and fund it from the facuet
    +
    +        Args:
    +            name (str): name of the wallet
    +
    +        """
    +        wallet = self.new(name)
    +        try:
    +            wallet.network = "TEST"
    +            wallet.activate_through_friendbot()
    +            wallet.add_known_trustline("TFT")
    +            wallet.save()
    +        except Exception as e:
    +            self.delete(name)
    +            raise j.exceptions.Runtime(f"Failed to create the wallet due to token service")
    +
    +        headers = {"Content-Type": "application/json"}
    +        url = "https://gettft.testnet.grid.tf/tft_faucet/api/transfer"
    +        data = {"destination": wallet.address}
    +
    +        try:
    +            response = requests.post(url, json=data, headers=headers)
    +        except requests.exceptions.HTTPError:
    +            self.delete(name)
    +            raise j.exceptions.Runtime(
    +                f"Failed to fund wallet can't reach {url} due to connection error. Changes will be reverted."
    +            )
    +
    +        if response.status_code != 200:
    +            self.delete(name)
    +            raise j.exceptions.Runtime(
    +                f"Failed to fund the wallet due to to facuet server error. Changes will be reverted."
    +            )
    +
    +        j.logger.info("Wallet created successfully")
    +        return True
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def check_stellar_service(self, networks=dict_keys(['TEST', 'STD'])) +
    +
    +

    This method will check if stellar and token service is up or not

    +
    + +Expand source code + +
    def check_stellar_service(self, networks=NETWORKS):
    +    """This method will check if stellar and token service is up or not"""
    +    for newtwork in networks:
    +        stellar_url = HORIZON_NETWORKS[newtwork]
    +        tokenservices_url = THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[newtwork]
    +
    +        # check stellar service
    +        try:
    +            j.tools.http.get(stellar_url)
    +        except RequestException:
    +            return False
    +
    +        # check token services
    +        try:
    +            j.tools.http.options(tokenservices_url)
    +        except RequestException:
    +            return False
    +
    +    return True
    +
    +
    +
    +def create_testnet_funded_wallet(self, name: str) ‑> bool +
    +
    +

    This method will create a testnet wallet and fund it from the facuet

    +

    Args

    +
    +
    name : str
    +
    name of the wallet
    +
    +
    + +Expand source code + +
    def create_testnet_funded_wallet(self, name: str) -> bool:
    +    """This method will create a testnet wallet and fund it from the facuet
    +
    +    Args:
    +        name (str): name of the wallet
    +
    +    """
    +    wallet = self.new(name)
    +    try:
    +        wallet.network = "TEST"
    +        wallet.activate_through_friendbot()
    +        wallet.add_known_trustline("TFT")
    +        wallet.save()
    +    except Exception as e:
    +        self.delete(name)
    +        raise j.exceptions.Runtime(f"Failed to create the wallet due to token service")
    +
    +    headers = {"Content-Type": "application/json"}
    +    url = "https://gettft.testnet.grid.tf/tft_faucet/api/transfer"
    +    data = {"destination": wallet.address}
    +
    +    try:
    +        response = requests.post(url, json=data, headers=headers)
    +    except requests.exceptions.HTTPError:
    +        self.delete(name)
    +        raise j.exceptions.Runtime(
    +            f"Failed to fund wallet can't reach {url} due to connection error. Changes will be reverted."
    +        )
    +
    +    if response.status_code != 200:
    +        self.delete(name)
    +        raise j.exceptions.Runtime(
    +            f"Failed to fund the wallet due to to facuet server error. Changes will be reverted."
    +        )
    +
    +    j.logger.info("Wallet created successfully")
    +    return True
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/stellar/stellar.html b/docs/api/jumpscale/clients/stellar/stellar.html new file mode 100644 index 000000000..053f7393d --- /dev/null +++ b/docs/api/jumpscale/clients/stellar/stellar.html @@ -0,0 +1,3725 @@ + + + + + + +jumpscale.clients.stellar.stellar API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.stellar.stellar

    +
    +
    +
    + +Expand source code + +
    import math
    +import base64
    +import time
    +from enum import Enum
    +import decimal
    +from urllib import parse
    +from urllib.parse import urlparse
    +from typing import Union
    +import gevent
    +import stellar_sdk
    +from jumpscale.clients.base import Client
    +from jumpscale.core.base import fields
    +from jumpscale.loader import j
    +from stellar_sdk import Asset, TransactionBuilder
    +
    +from .wrapped import Account, Server
    +from .balance import AccountBalances, Balance, EscrowAccount, VestingAccount
    +from .transaction import Effect, PaymentSummary, TransactionSummary
    +from .exceptions import UnAuthorized
    +from .vesting import is_vesting_account, VESTING_SCHEME
    +
    +
    +XLM_TRANSACTION_FEES = 0.00001
    +ACTIVATION_ADDRESS = "GCKLGWHEYT2V63HC2VDJRDWEY3G54YSHHPOA6Q3HAPQUGA5OZDWZL7KW"
    +
    +_THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES = {
    +    "TEST": "testnet.threefold.io",
    +    "STD": "tokenservices.threefold.io",
    +}
    +_HORIZON_NETWORKS = {
    +    "TEST": "https://horizon-testnet.stellar.org",
    +    "STD": "https://horizon.stellar.org",
    +}
    +_NETWORK_PASSPHRASES = {
    +    "TEST": stellar_sdk.Network.TESTNET_NETWORK_PASSPHRASE,
    +    "STD": stellar_sdk.Network.PUBLIC_NETWORK_PASSPHRASE,
    +}
    +_NETWORK_KNOWN_TRUSTS = {
    +    "TEST": {
    +        "TFT": "GA47YZA3PKFUZMPLQ3B5F2E3CJIB57TGGU7SPCQT2WAEYKN766PWIMB3",
    +        "FreeTFT": "GBLDUINEFYTF7XEE7YNWA3JQS4K2VD37YU7I2YAE7R5AHZDKQXSS2J6R",
    +        "TFTA": "GB55A4RR4G2MIORJTQA4L6FENZU7K4W7ATGY6YOT2CW47M5SZYGYKSCT",
    +    },
    +    "STD": {
    +        "TFT": "GBOVQKJYHXRR3DX6NOX2RRYFRCUMSADGDESTDNBDS6CDVLGVESRTAC47",
    +        "FreeTFT": "GCBGS5TFE2BPPUVY55ZPEMWWGR6CLQ7T6P46SOFGHXEBJ34MSP6HVEUT",
    +        "TFTA": "GBUT4GP5GJ6B3XW5PXENHQA7TXJI5GOPW3NF4W3ZIW6OOO4ISY6WNLN2",
    +    },
    +}
    +_THREEFOLDFOUNDATION_TFTSTELLAR_ENDPOINT = {
    +    "FUND": "/threefoldfoundation/transactionfunding_service/fund_transaction",
    +    "CREATE_UNLOCK": "/threefoldfoundation/unlock_service/create_unlockhash_transaction",
    +    "GET_UNLOCK": "/threefoldfoundation/unlock_service/get_unlockhash_transaction",
    +    "CREATE_ACTIVATION_CODE": "/threefoldfoundation/activation_service/create_activation_code",
    +    "ACTIVATE_ACCOUNT": "/threefoldfoundation/activation_service/activate_account",
    +}
    +
    +
    +class Network(Enum):
    +    STD = "STD"
    +    TEST = "TEST"
    +
    +
    +class Stellar(Client):
    +    network = fields.Enum(Network)
    +    address = fields.String()
    +    sequence = fields.Integer()
    +    sequencedate = fields.Integer()
    +
    +    def secret_updated(self, value):
    +        self.address = stellar_sdk.Keypair.from_secret(value).public_key
    +
    +    secret = fields.String(on_update=secret_updated)
    +
    +    def _get_horizon_server(self):
    +        server_url = _HORIZON_NETWORKS[self.network.value]
    +        server = Server(horizon_url=server_url)
    +        return server
    +
    +    def _get_free_balances(self, address=None):
    +        address = address or self.address
    +        balances = AccountBalances(address)
    +        response = self._get_horizon_server().accounts().account_id(address).call()
    +        for response_balance in response["balances"]:
    +            balance = Balance.from_horizon_response(response_balance)
    +            if balance is not None:
    +                balances.add_balance(balance)
    +        return balances
    +
    +    def load_account(self):
    +        horizonServer = self._get_horizon_server()
    +        saccount = horizonServer.load_account(self.address)
    +        account = Account(saccount.account.account_id, saccount.sequence, self)
    +        return account
    +
    +    def _get_url(self, endpoint):
    +        url = _THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[self.network.value]
    +        if not j.sals.nettools.wait_connection_test(url, 443, 5):
    +            raise j.exceptions.Timeout(f"Can not connect to server {url}, connection timeout")
    +        endpoint = _THREEFOLDFOUNDATION_TFTSTELLAR_ENDPOINT[endpoint]
    +        return f"https://{url}{endpoint}"
    +
    +    def _fund_transaction(self, transaction):
    +        data = {"transaction": transaction}
    +        resp = j.tools.http.post(self._get_url("FUND"), json={"args": data})
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def _create_unlockhash_transaction(self, unlock_hash, transaction_xdr):
    +        data = {"unlockhash": unlock_hash, "transaction_xdr": transaction_xdr}
    +        resp = j.tools.http.post(self._get_url("CREATE_UNLOCK"), json={"args": data})
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def _get_unlockhash_transaction(self, unlockhash):
    +        data = {"unlockhash": unlockhash}
    +        resp = j.tools.http.post(self._get_url("GET_UNLOCK"), json={"args": data})
    +        if resp.status_code == j.tools.http.status_codes.codes.NOT_FOUND:
    +            return None
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def _create_activation_code(self):
    +        data = {"address": self.address}
    +        resp = j.tools.http.post(self._get_url("CREATE_ACTIVATION_CODE"), json={"args": data})
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def _activation_account(self):
    +        resp = j.tools.http.post(self._get_url("ACTIVATE_ACCOUNT"), json={"address": self.address})
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def set_unlock_transaction(self, unlock_transaction):
    +        """
    +        Adds a xdr encoded unlocktransaction
    +        :param unlock_transaction: xdr encoded unlocktransactionaddress of the destination.
    +        :type destination_address: str
    +        """
    +        txe = stellar_sdk.TransactionEnvelope.from_xdr(unlock_transaction, _NETWORK_PASSPHRASES[self.network.value])
    +        tx_hash = txe.hash()
    +        unlock_hash = stellar_sdk.strkey.StrKey.encode_pre_auth_tx(tx_hash)
    +
    +        self._create_unlockhash_transaction(unlock_hash=unlock_hash, transaction_xdr=txe.to_xdr())
    +
    +    def get_balance(self, address=None):
    +        """Gets the balances for a stellar address"""
    +        if address is None:
    +            address = self.address
    +        all_balances = self._get_free_balances(address)
    +        for account in self._find_escrow_accounts(address):
    +            all_balances.add_escrow_account(account)
    +        return all_balances
    +
    +    def _find_escrow_accounts(self, address=None):
    +        if address is None:
    +            address = self.address
    +        escrow_accounts = []
    +        accounts_endpoint = self._get_horizon_server().accounts()
    +        accounts_endpoint.for_signer(address)
    +        old_cursor = "old"
    +        new_cursor = ""
    +        while new_cursor != old_cursor:
    +            old_cursor = new_cursor
    +            accounts_endpoint.cursor(new_cursor)
    +            response = accounts_endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            cursor = parse.parse_qs(next_link_query).get("cursor")
    +            if not cursor:
    +                break
    +            new_cursor = cursor[0]
    +            accounts = response["_embedded"]["records"]
    +            for account in accounts:
    +                account_id = account["account_id"]
    +                if account_id == address:
    +                    continue  # Do not take the receiver's account
    +                all_signers = account["signers"]
    +                preauth_signers = [signer["key"] for signer in all_signers if signer["type"] == "preauth_tx"]
    +                # TODO check the tresholds and signers
    +                # TODO if we can merge, the amount is unlocked ( if len(preauth_signers))==0
    +                balances = []
    +                for response_balance in account["balances"]:
    +                    balances.append(Balance.from_horizon_response(response_balance))
    +                if is_vesting_account(
    +                    account,
    +                    address,
    +                    self.network.value,
    +                    _NETWORK_PASSPHRASES[self.network.value],
    +                    self._get_unlockhash_transaction,
    +                ):
    +                    escrow_accounts.append(VestingAccount(account_id, balances, VESTING_SCHEME))
    +                else:
    +                    escrow_accounts.append(
    +                        EscrowAccount(
    +                            account_id,
    +                            preauth_signers,
    +                            balances,
    +                            _NETWORK_PASSPHRASES[self.network.value],
    +                            self._get_unlockhash_transaction,
    +                        )
    +                    )
    +        return escrow_accounts
    +
    +    def claim_locked_funds(self):
    +        balances = self.get_balance()
    +        for locked_account in balances.escrow_accounts:
    +            if locked_account.can_be_unlocked():
    +                self._unlock_account(locked_account)
    +
    +    def _unlock_account(self, escrow_account):
    +        submitted_unlock_transactions = 0
    +        for unlockhash in escrow_account.unlockhashes:
    +            unlockhash_transation = self._get_unlockhash_transaction(unlockhash=unlockhash)
    +            if unlockhash_transation is None:
    +                return
    +            j.logger.info(unlockhash_transation["transaction_xdr"])
    +            self._get_horizon_server().submit_transaction(unlockhash_transation["transaction_xdr"])
    +            submitted_unlock_transactions += 1
    +
    +        if submitted_unlock_transactions == len(escrow_account.unlockhashes):
    +            self._merge_account(escrow_account.address)
    +
    +    def _merge_account(self, address):
    +        server = self._get_horizon_server()
    +        account = server.load_account(address)
    +        # Increment the sequence number in case the unlock transaction was not processed before the load_account call
    +        # account.increment_sequence_number()
    +        balances = self._get_free_balances(address)
    +        base_fee = server.fetch_base_fee()
    +        transaction_builder = stellar_sdk.TransactionBuilder(
    +            source_account=account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee
    +        )
    +        for balance in balances.balances:
    +            if balance.is_native():
    +                continue
    +            # Step 1: Transfer custom assets
    +            if decimal.Decimal(balance.balance) > decimal.Decimal(0):
    +                transaction_builder.append_payment_op(
    +                    destination=self.address,
    +                    amount=balance.balance,
    +                    asset_code=balance.asset_code,
    +                    asset_issuer=balance.asset_issuer,
    +                    source=account.account.account_id,
    +                )
    +            # Step 2: Delete trustlines
    +            transaction_builder.append_change_trust_op(
    +                asset_issuer=balance.asset_issuer, asset_code=balance.asset_code, limit="0"
    +            )
    +        # Step 3: Merge account
    +        transaction_builder.append_account_merge_op(self.address)
    +
    +        transaction_builder.set_timeout(30)
    +        transaction = transaction_builder.build()
    +        signer_kp = stellar_sdk.Keypair.from_secret(self.secret)
    +        transaction.sign(signer_kp)
    +        server.submit_transaction(transaction)
    +
    +    def activate_through_friendbot(self):
    +        """Activates and funds a testnet account using friendbot"""
    +        if self.network.value != "TEST":
    +            raise Exception("Account activation through friendbot is only available on testnet")
    +
    +        resp = j.tools.http.get("https://friendbot.stellar.org", params={"addr": self.address})
    +        resp.raise_for_status()
    +        j.logger.info(f"account with address {self.address} activated and funded through friendbot")
    +
    +    def activate_through_threefold_service(self):
    +        """
    +        Activate your wallet through threefold services
    +        """
    +        for _ in range(5):
    +            j.logger.info(f"trying to activate : {self.instance_name}")
    +            try:
    +                resp = self._activation_account()
    +                loaded_json = j.data.serializers.json.loads(resp)
    +                xdr = loaded_json["activation_transaction"]
    +                self.sign(xdr, submit=True)
    +                j.logger.info(f"{self.instance_name} is activated using the activation service.")
    +                return
    +            except Exception as e:
    +                j.logger.error(f"failed to activate using the activation service {e}")
    +                ## Try activating with `activation_wallet` j.clients.stellar.activation_wallet if exists
    +                ## this activator should be imported on the system.
    +        else:
    +            raise RuntimeError(
    +                "could not activate wallet. tried activation service and there's no activation_wallet configured on the system"
    +            )
    +
    +    def activate_through_activation_wallet(self, wallet_name="activation_wallet"):
    +        """Activate your wallet through activation wallet."""
    +        if wallet_name in j.clients.stellar.list_all() and self.instance_name != wallet_name:
    +            j.logger.info(f"trying to fund the wallet ourselves with the activation wallet")
    +            j.logger.info(f"activation wallet {self.instance_name}")
    +            for _ in range(5):
    +                try:
    +                    j.clients.stellar.activation_wallet.activate_account(self.address, "2.6")
    +                    self.add_known_trustline("TFT")
    +                    j.logger.info(f"activated wallet {self.instance_name}")
    +                    return
    +                except Exception as e:
    +                    j.logger.error(f"failed to activate wallet {self.instance_name} using activation_wallet")
    +        else:
    +            raise RuntimeError(f"could not find the activation wallet: {wallet_name}")
    +
    +    def activate_account(self, destination_address, starting_balance="3.6"):
    +        """Activates another account
    +
    +        Args:
    +            destination_address (str): address of the destination
    +            starting_balance (str, optional): the balance that the destination address will start with. Must be a positive integer expressed as a string. Defaults to "12.50".
    +        """
    +        server = self._get_horizon_server()
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +        source_account = self.load_account()
    +
    +        base_fee = server.fetch_base_fee()
    +        transaction = (
    +            stellar_sdk.TransactionBuilder(
    +                source_account=source_account,
    +                network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +                base_fee=base_fee,
    +            )
    +            .append_create_account_op(destination=destination_address, starting_balance=starting_balance)
    +            .build()
    +        )
    +        transaction.sign(source_keypair)
    +        try:
    +            response = server.submit_transaction(transaction)
    +            j.logger.info("Transaction hash: {}".format(response["hash"]))
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            j.logger.debug(e)
    +
    +    def add_trustline(self, asset_code, issuer, secret=None):
    +        """Create a trustline to an asset
    +
    +        Args:
    +            asset_code (str): code of the asset. For example: 'BTC', 'TFT', ...
    +            issuer (str): address of the asset issuer
    +            secret (str, optional): Secret to use will use instance property if empty. Defaults to None.
    +        """
    +        self._change_trustline(asset_code, issuer, secret=secret)
    +        j.logger.info(f"Added trustline {asset_code}:{issuer} to account {self.address}")
    +
    +    def add_known_trustline(self, asset_code):
    +        """Will add a trustline known by threefold for chosen asset_code
    +
    +        Args:
    +            asset_code (str): code of the asset. For example: 'BTC', 'TFT', ...
    +        """
    +        j.logger.info(f"adding trustline {asset_code} to account {self.address}")
    +        balances = self.get_balance()
    +        for b in balances.balances:
    +            if b.asset_code == asset_code:
    +                j.logger.info(f"trustline {asset_code} is already added.")
    +                return
    +        issuer = _NETWORK_KNOWN_TRUSTS.get(self.network.value, {}).get(asset_code)
    +        if not issuer:
    +            raise j.exceptions.NotFound(f"There is no known issuer for {asset_code} on network {self.network}")
    +        self._change_trustline(asset_code, issuer)
    +
    +    def delete_trustline(self, asset_code, issuer, secret=None):
    +        """Deletes a trustline
    +
    +        Args:
    +            asset_code (str): code of the asset. For example: 'BTC', 'XRP', ...
    +            issuer (str): address of the asset issuer
    +            secret (str, optional): Secret to use will use instance property if empty. Defaults to None.
    +        """
    +        self._change_trustline(asset_code, issuer, limit="0", secret=secret)
    +        j.logger.info(f"Removed trustline {asset_code}:{issuer} from account {self.address}")
    +
    +    def _change_trustline(self, asset_code, issuer, limit=None, secret=None):
    +        """Create a trustline between you and the issuer of an asset
    +
    +        Args:
    +            asset_code (str): code which form the asset. For example: 'BTC', 'TFT', ...
    +            issuer (str): address of the asset issuer
    +            limit ([type], optional): The limit for the asset, defaults to max int64(922337203685.4775807). If the limit is set to “0” it deletes the trustline. Defaults to None.
    +            secret (str, optional): Secret to use will use instance property if empty. Defaults to None.
    +        """
    +        # if no secret is provided we assume we change trustlines for this account
    +        secret = secret or self.secret
    +
    +        server = self._get_horizon_server()
    +        source_keypair = stellar_sdk.Keypair.from_secret(secret)
    +        source_public_key = source_keypair.public_key
    +        source_account = server.load_account(source_public_key)
    +
    +        base_fee = server.fetch_base_fee()
    +        transaction = (
    +            stellar_sdk.TransactionBuilder(
    +                source_account=source_account,
    +                network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +                base_fee=base_fee,
    +            )
    +            .append_change_trust_op(Asset(asset_code,issuer), limit=limit)
    +            .set_timeout(30)
    +            .build()
    +        )
    +
    +        transaction.sign(source_keypair)
    +
    +        try:
    +            server.submit_transaction(transaction)
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            j.logger.debug(e)
    +            raise e
    +
    +    def return_xlms_to_activation(self):
    +        xlm_balance = 0
    +        for balance in self.get_balances():
    +            if balance.asset_code == "XLM":
    +                xlm_balance = balance.balance
    +        trustlines = len(self.get_balances()) - 1
    +        minimum_balance = 1 + 0.5 * trustlines
    +        amount = xlm_balance - minimum_balance - XLM_TRANSACTION_FEES
    +        self.transfer(ACTIVATION_ADDRESS, amount)
    +
    +    def transfer(
    +        self,
    +        destination_address,
    +        amount,
    +        asset="XLM",
    +        locked_until=None,
    +        memo_text=None,
    +        memo_hash=None,
    +        fund_transaction=True,
    +        from_address=None,
    +        timeout=30,
    +        sequence_number: int = None,
    +        sign: bool = True,
    +        retries: int = 5,
    +    ):
    +        """Transfer assets to another address
    +
    +        Args:
    +            destination_address (str): address of the destination
    +            amount (str): can be a floating point number with 7 numbers after the decimal point expressed as a string
    +            asset (str, optional): asset to transfer. Defaults to "XLM". if you wish to specify an asset it should be in format 'assetcode:issuer'. Where issuer is the address of the
    +            issuer of the asset.
    +            locked_until (float, optional): epoch timestamp indicating until when the tokens  should be locked. Defaults to None.
    +            memo_text (Union[str, bytes], optional): memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long. Defaults to None.
    +            memo_hash (Union[str, bytes], optional): memo hash to add to the transaction, A 32 byte hash. Defaults to None.
    +            fund_transaction (bool, optional): use the threefoldfoundation transaction funding service. Defautls to True.
    +            from_address (str, optional): Use a different address to send the tokens from, useful in multisig use cases. Defaults to None.
    +            timeout (int,optional: Seconds from now on until when the transaction to be submitted to the stellar network
    +            sequence_number (int,optional): specify a specific sequence number ( will still be increased by one) instead of loading it from the account
    +            sign (bool,optional) : Do not sign and submit the transaction
    +
    +        Raises:
    +            Exception: If asset not in correct format
    +            stellar_sdk.exceptions.BadRequestError: not enough funds for opertaion
    +            stellar_sdk.exceptions.BadRequestError: bad transfer authentication
    +
    +        Returns:
    +            [type]: [description]
    +        """
    +        if decimal.Decimal(amount) <= 0:
    +            j.logger.warning("Can not transfer empty or zero amount transaction")
    +            return
    +        nretries = 0
    +        while nretries < retries:
    +            try:
    +                return self._transfer(
    +                    destination_address=destination_address,
    +                    amount=amount,
    +                    asset=asset,
    +                    locked_until=locked_until,
    +                    memo_text=memo_text,
    +                    memo_hash=memo_hash,
    +                    fund_transaction=fund_transaction,
    +                    from_address=from_address,
    +                    timeout=timeout,
    +                    sequence_number=sequence_number,
    +                    sign=sign,
    +                )
    +            except Exception as e:
    +                nretries += 1
    +                gevent.sleep(1)
    +                j.logger.warning(str(e))
    +
    +        raise j.exceptions.Runtime(f"Failed to make transaction for {retries} times, Please try again later")
    +
    +    def _transfer(
    +        self,
    +        destination_address,
    +        amount,
    +        asset="XLM",
    +        locked_until=None,
    +        memo_text=None,
    +        memo_hash=None,
    +        fund_transaction=True,
    +        from_address=None,
    +        timeout=30,
    +        sequence_number: int = None,
    +        sign: bool = True,
    +    ):
    +        issuer = None
    +        j.logger.info(f"Sending {amount} {asset} from {self.address} to {destination_address}")
    +        if asset != "XLM":
    +            assetStr = asset.split(":")
    +            if len(assetStr) != 2:
    +                raise Exception(f"Wrong asset format should be in format 'assetcode:issuer', but received {assetStr}")
    +            asset_code = assetStr[0]
    +            issuer = assetStr[1]
    +        else:
    +            asset_code = asset
    +
    +        if locked_until is not None:
    +            return self._transfer_locked_tokens(
    +                destination_address,
    +                amount,
    +                asset_code,
    +                issuer,
    +                locked_until,
    +                memo_text=memo_text,
    +                memo_hash=memo_hash,
    +                fund_transaction=fund_transaction,
    +                from_address=from_address,
    +            )
    +
    +        horizon_server = self._get_horizon_server()
    +
    +        base_fee = horizon_server.fetch_base_fee()
    +        if from_address:
    +            source_account = horizon_server.load_account(from_address)
    +        else:
    +            source_account = self.load_account()
    +
    +        if sequence_number:
    +            source_account.sequence = sequence_number
    +
    +        transaction_builder = stellar_sdk.TransactionBuilder(
    +            source_account=source_account,
    +            network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +            base_fee=base_fee,
    +        )
    +        transaction_builder.append_payment_op(
    +            destination=destination_address,
    +            amount=str(amount),
    +            asset=self._get_asset(asset_code),
    +            source=source_account.account.account_id,
    +        )
    +        transaction_builder.set_timeout(timeout)
    +        if memo_text is not None:
    +            transaction_builder.add_text_memo(memo_text)
    +        if memo_hash is not None:
    +            transaction_builder.add_hash_memo(memo_hash)
    +
    +        transaction = transaction_builder.build()
    +        transaction = transaction.to_xdr()
    +
    +        if asset_code in _NETWORK_KNOWN_TRUSTS[self.network.value]:
    +            if fund_transaction:
    +                transaction = self._fund_transaction(transaction=transaction)
    +                transaction = transaction["transaction_xdr"]
    +
    +        if not sign:
    +            return transaction
    +
    +        transaction = stellar_sdk.TransactionEnvelope.from_xdr(transaction, _NETWORK_PASSPHRASES[self.network.value])
    +
    +        my_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +        transaction.sign(my_keypair)
    +
    +        response = horizon_server.submit_transaction(transaction)
    +        tx_hash = response["hash"]
    +        j.logger.info(f"Transaction hash: {tx_hash}")
    +        return tx_hash
    +
    +    def list_payments(self, address: str = None, asset: str = None, cursor: str = None):
    +        """Get the transactions for an adddress
    +        :param address: address of the effects.In None, the address of this wallet is taken
    +        :param asset: stellar asset in the code:issuer form( except for XLM, which does not need an issuer)
    +        :param cursor:pass a cursor to continue after the last call or an empty str to start receivibg a cursor
    +         if a cursor is passed, a tuple of the payments and the cursor is returned
    +        """
    +        if address is None:
    +            address = self.address
    +        tx_endpoint = self._get_horizon_server().payments()
    +        tx_endpoint.for_account(address)
    +        tx_endpoint.limit(50)
    +        payments = []
    +        old_cursor = "old"
    +        new_cursor = ""
    +        if cursor is not None:
    +            new_cursor = cursor
    +        while old_cursor != new_cursor:
    +            old_cursor = new_cursor
    +            tx_endpoint.cursor(new_cursor)
    +            response = tx_endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +            response_payments = response["_embedded"]["records"]
    +            for response_payment in response_payments:
    +                ps = PaymentSummary.from_horizon_response(response_payment, address)
    +                if asset:
    +                    split_asset = asset.split(":")
    +                    assetcode = split_asset[0]
    +                    assetissuer = None
    +                    if len(split_asset) > 1:
    +                        assetissuer = split_asset[1]
    +                    if ps.balance and ps.balance.asset_code == assetcode:
    +                        if assetissuer and assetissuer == ps.balance.asset_issuer:
    +                            payments.append(ps)
    +                else:
    +                    payments.append(ps)
    +        if cursor is not None:
    +            return {"payments": payments, "cursor": new_cursor}
    +
    +        return payments
    +
    +    def list_transactions(self, address: str = None, cursor: str = None):
    +        """Get the transactions for an adddres
    +        :param address (str, optional): address of the effects.If None, the address of this wallet is taken. Defaults to None.
    +        :param cursor:pass a cursor to continue after the last call or an empty str to start receivibg a cursor
    +         if a cursor is passed, a tuple of the payments and the cursor is returned
    +
    +        Returns:
    +            list: list of TransactionSummary objects
    +            dictionary: {"transactions":list of TransactionSummary objects, "cursor":cursor}
    +        """
    +        address = address or self.address
    +        tx_endpoint = self._get_horizon_server().transactions()
    +        tx_endpoint.for_account(address)
    +        tx_endpoint.include_failed(True)
    +        transactions = []
    +        old_cursor = "old"
    +        new_cursor = ""
    +        if cursor is not None:
    +            new_cursor = cursor
    +        while old_cursor != new_cursor:
    +            old_cursor = new_cursor
    +            tx_endpoint.cursor(new_cursor)
    +            response = tx_endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +            response_transactions = response["_embedded"]["records"]
    +            for response_transaction in response_transactions:
    +                if response_transaction["successful"]:
    +                    transactions.append(TransactionSummary.from_horizon_response(response_transaction))
    +
    +        if cursor is not None:
    +            return {"transactions": transactions, "cursor": new_cursor}
    +        return transactions
    +
    +    def get_transaction_effects(self, transaction_hash, address=None):
    +        """Get the effects on an adddressfor a specific transaction
    +
    +        Args:
    +            transaction_hash (str): hash of the transaction
    +            address (str, optional): address of the effects.If None, the address of this wallet is taken. Defaults to None.
    +
    +        Returns:
    +            list: list of Effect objects
    +        """
    +        address = address or self.address
    +        effects = []
    +        endpoint = self._get_horizon_server().effects()
    +        endpoint.for_transaction(transaction_hash)
    +        old_cursor = "old"
    +        new_cursor = ""
    +        while old_cursor != new_cursor:
    +            old_cursor = new_cursor
    +            endpoint.cursor(new_cursor)
    +            response = endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +            response_effects = response["_embedded"]["records"]
    +            for response_effect in response_effects:
    +                if "account" in response_effect and response_effect["account"] == address:
    +                    effects.append(Effect.from_horizon_response(response_effect))
    +        return effects
    +
    +    def _transfer_locked_tokens(
    +        self,
    +        destination_address,
    +        amount,
    +        asset_code,
    +        asset_issuer,
    +        unlock_time,
    +        memo_text=None,
    +        memo_hash=None,
    +        fund_transaction=True,
    +        from_address=None,
    +    ):
    +        """Transfer locked assets to another address
    +
    +        Args:
    +            destination_address (str): address of the destination
    +            amount (str): amount, can be a floating point number with 7 numbers after the decimal point expressed as a string
    +            asset_code (str): asset to transfer
    +            asset_issuer (str): if the asset_code is different from 'XlM', the issuer address
    +            unlock_time (float):  an epoch timestamp indicating when the funds should be unlocked
    +            memo_text (Union[str, bytes], optional): memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long
    +            memo_hash (Union[str, bytes], optional): memo hash to add to the transaction, A 32 byte hash
    +            fund_transaction (bool, optional): use the threefoldfoundation transaction funding service.Defaults to True.
    +            from_address (str, optional): Use a different address to send the tokens from, useful in multisig use cases. Defaults to None.
    +
    +        Returns:
    +            [type]: [description]
    +        """
    +
    +        unlock_time = math.ceil(unlock_time)
    +
    +        j.logger.info("Creating escrow account")
    +        escrow_kp = stellar_sdk.Keypair.random()
    +
    +        # minimum account balance as described at https://www.stellar.org/developers/guides/concepts/fees.html#minimum-account-balance
    +        horizon_server = self._get_horizon_server()
    +        base_fee = horizon_server.fetch_base_fee()
    +        base_reserve = 0.5
    +        minimum_account_balance = (2 + 1 + 3) * base_reserve  # 1 trustline and 3 signers
    +        required_XLM = minimum_account_balance + base_fee * 0.0000001 * 3
    +
    +        j.logger.info("Activating escrow account")
    +        self.activate_account(escrow_kp.public_key, str(math.ceil(required_XLM)))
    +
    +        if asset_code != "XLM":
    +            j.logger.info("Adding trustline to escrow account")
    +            self.add_trustline(asset_code, asset_issuer, escrow_kp.secret)
    +
    +        preauth_tx = self._create_unlock_transaction(escrow_kp, unlock_time)
    +        preauth_tx_hash = preauth_tx.hash()
    +
    +        # save the preauth transaction in our unlock service
    +        unlock_hash = stellar_sdk.strkey.StrKey.encode_pre_auth_tx(preauth_tx_hash)
    +        self._create_unlockhash_transaction(unlock_hash=unlock_hash, transaction_xdr=preauth_tx.to_xdr())
    +
    +        self._set_account_signers(escrow_kp.public_key, destination_address, preauth_tx_hash, escrow_kp)
    +        j.logger.info(preauth_tx.to_xdr())
    +
    +        self.transfer(
    +            escrow_kp.public_key,
    +            amount,
    +            asset_code + ":" + asset_issuer,
    +            memo_text=memo_text,
    +            memo_hash=memo_hash,
    +            fund_transaction=fund_transaction,
    +            from_address=from_address,
    +        )
    +        return preauth_tx.to_xdr()
    +
    +    def _create_unlock_transaction(self, escrow_kp, unlock_time):
    +        server = self._get_horizon_server()
    +        escrow_account = server.load_account(escrow_kp.public_key)
    +        escrow_account.increment_sequence_number()
    +        tx = (
    +            stellar_sdk.TransactionBuilder(escrow_account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +            .append_set_options_op(master_weight=0, low_threshold=1, med_threshold=1, high_threshold=1)
    +            .add_time_bounds(unlock_time, 0)
    +            .build()
    +        )
    +        tx.sign(escrow_kp)
    +        return tx
    +
    +    def _set_account_signers(self, address, public_key_signer, preauth_tx_hash, signer_kp):
    +        server = self._get_horizon_server()
    +        if address == self.address:
    +            account = self.load_account()
    +        else:
    +            account = server.load_account(address)
    +        tx = (
    +            stellar_sdk.TransactionBuilder(account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +            .append_pre_auth_tx_signer(preauth_tx_hash, 1)
    +            .append_ed25519_public_key_signer(public_key_signer, 1)
    +            .append_set_options_op(master_weight=1, low_threshold=2, med_threshold=2, high_threshold=2)
    +            .build()
    +        )
    +
    +        tx.sign(signer_kp)
    +        response = server.submit_transaction(tx)
    +        j.logger.info(response)
    +        j.logger.info(f"Set the signers of {address} to {public_key_signer} and {preauth_tx_hash}")
    +
    +    def get_signing_requirements(self, address: str = None):
    +        address = address or self.address
    +        response = self._get_horizon_server().accounts().account_id(address).call()
    +        signing_requirements = {}
    +        signing_requirements["thresholds"] = response["thresholds"]
    +        signing_requirements["signers"] = response["signers"]
    +        return signing_requirements
    +
    +    def modify_signing_requirements(
    +        self, public_keys_signers, signature_count, low_treshold=0, high_treshold=2, master_weight=2
    +    ):
    +        """modify_signing_requirements sets to amount of signatures required for the creation of multisig account. It also adds
    +           the public keys of the signer to this account
    +
    +        Args:
    +            public_keys_signers (list): list of public keys of signers
    +            signature_count (int): amount of signatures requires to transfer funds
    +            low_treshold (int, optional): amount of signatures required for low security operations (transaction processing, allow trust, bump sequence). Defaults to 1.
    +            high_treshold (int, optional): amount of signatures required for high security operations (set options, account merge). Defaults to 2.
    +            master_weight (int, optional): A number from 0-255 (inclusive) representing the weight of the master key. If the weight of the master key is updated to 0, it is effectively disabled. Defaults to 2.
    +        """
    +        server = self._get_horizon_server()
    +        account = self.load_account()
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +        transaction_builder = stellar_sdk.TransactionBuilder(
    +            account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value]
    +        )
    +        # set the signing options
    +        transaction_builder.append_set_options_op(
    +            low_threshold=low_treshold,
    +            med_threshold=signature_count,
    +            high_threshold=high_treshold,
    +            master_weight=master_weight,
    +        )
    +
    +        # For every public key given, add it as a signer to this account
    +        for public_key_signer in public_keys_signers:
    +            transaction_builder.append_ed25519_public_key_signer(public_key_signer, 1)
    +
    +        transaction_builder.set_timeout(30)
    +        tx = transaction_builder.build()
    +        tx.sign(source_keypair)
    +
    +        try:
    +            response = server.submit_transaction(tx)
    +            j.logger.info(response)
    +            j.logger.info(f"Set the signers of {self.address} to require {signature_count} signers")
    +        except stellar_sdk.exceptions.BadRequestError:
    +            j.logger.info("Transaction need additional signatures in order to send")
    +            return tx.to_xdr()
    +
    +    def sign(self, tx_xdr: str, submit: bool = True):
    +        """sign signs a transaction xdr and optionally submits it to the network
    +
    +        Args:
    +            tx_xdr (str): transaction to sign in xdr format
    +            submit (bool,optional): submit the transaction tro the Stellar network
    +        """
    +
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +        tx = stellar_sdk.TransactionEnvelope.from_xdr(tx_xdr, _NETWORK_PASSPHRASES[self.network.value])
    +        tx.sign(source_keypair)
    +        if submit:
    +            horizon_server = self._get_horizon_server()
    +            horizon_server.submit_transaction(tx)
    +        else:
    +            return tx.to_xdr()
    +
    +    def sign_multisig_transaction(self, tx_xdr):
    +        """sign_multisig_transaction signs a transaction xdr and tries to submit it to the network
    +
    +           Deprecated, use sign instead
    +
    +        Args:
    +            tx_xdr (str): transaction to sign in xdr format
    +        """
    +
    +        try:
    +            self.sign(tx_xdr)
    +            j.logger.info("Multisig tx signed and sent")
    +        except UnAuthorized as e:
    +            j.logger.info("Transaction needs additional signatures in order to send")
    +            return e.transaction_xdr
    +
    +    def remove_signer(self, public_key_signer):
    +        """remove_signer removes a public key as a signer from the source account
    +
    +        Args:
    +            public_key_signer (str): public key of an account
    +        """
    +        server = self._get_horizon_server()
    +        account = self.load_account()
    +        tx = (
    +            stellar_sdk.TransactionBuilder(account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +            .append_ed25519_public_key_signer(public_key_signer, 0)
    +            .build()
    +        )
    +
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +        tx.sign(source_keypair)
    +        try:
    +            response = server.submit_transaction(tx)
    +            j.logger.info(response)
    +            j.logger.info("Multisig tx signed and sent")
    +        except stellar_sdk.exceptions.BadRequestError:
    +            j.logger.info("Transaction need additional signatures in order to send")
    +            return tx.to_xdr()
    +
    +    def get_sender_wallet_address(self, transaction_hash):
    +        """Get the sender's wallet address from a transaction hash
    +
    +        Args:
    +            transaction_hash (String): Transaction hash
    +
    +        Returns:
    +            String : Wallet Hash
    +
    +        """
    +        server = self._get_horizon_server()
    +        endpoint = server.operations().for_transaction(transaction_hash)
    +        response = endpoint.call()
    +        # not possible for a transaction to have more than a source, so will take first one
    +        wallet_address = response["_embedded"]["records"][0]["source_account"]
    +        return wallet_address
    +
    +    def check_is_payment_transaction(self, transaction_hash):
    +        """Some transactions doesn't have an amount like activating the wallet
    +        This helper method to help in iterating in transactions
    +
    +        Args:
    +            transaction_hash (String): Transaction hash
    +
    +        Returns:
    +            Bool: True if transaction has amount - False if not
    +        """
    +
    +        server = self._get_horizon_server()
    +        endpoint = server.operations().for_transaction(transaction_hash)
    +        response = endpoint.call()
    +        results = response["_embedded"]["records"][0]
    +        return results["type"] == "payment"
    +
    +    def _get_asset(self, code="TFT", issuer=None) -> stellar_sdk.Asset:
    +        """Gets an stellar_sdk.Asset object by code.
    +        if the code is TFT or TFTA we quickly return the Asset object based on the code.
    +        if the code is native (XLM) we return the Asset object with None issuer.
    +        if the code isn't unknown, exception is raised to manually construct the Asset object.
    +
    +        Args:
    +            code (str, optional): code for the asset. Defaults to "TFT".
    +            issuer (str, optional): issuer for the asset. Defaults to None.
    +
    +        Raises:
    +            ValueError: empty code, In case of issuer is None and not XLM or the code isn't for TFT or TFTA.
    +            stellar_sdk.exceptions.AssetIssuerInvalidError: Invalid issuer
    +        Returns:
    +            stellar_sdk.Asset: Asset object.
    +        """
    +        network = self.network.value
    +        KNOWN_ASSETS = list(_NETWORK_KNOWN_TRUSTS[network].keys()) + ["XLM"]
    +
    +        if issuer and code:
    +            return Asset(code, issuer)
    +
    +        if not code:
    +            raise ValueError("An asset code is required")
    +
    +        if not issuer and code not in KNOWN_ASSETS:
    +            raise ValueError(
    +                f"Make sure to supply the issuer for {code}, issuer is allowed to be none only in case of {KNOWN_ASSETS}"
    +            )
    +
    +        if not issuer and code in KNOWN_ASSETS:
    +            asset_issuer = _NETWORK_KNOWN_TRUSTS[network].get(code, None)
    +            return Asset(code, asset_issuer)
    +
    +    def cancel_sell_order(self, offer_id, selling_asset: str, buying_asset: str, price: Union[str, decimal.Decimal]):
    +        """Deletes a selling order for amount `amount` of `selling_asset` for `buying_asset` with the price of `price`
    +
    +        Args:
    +            selling_asset (str): Selling Asset
    +            buying_asset (str): Buying Asset
    +            offer_id (int): pass the current offer id and set the amount to 0 to cancel this offer
    +            price (str): order price
    +        """
    +        return self._manage_sell_order(
    +            selling_asset=selling_asset, buying_asset=buying_asset, amount="0", price=price, offer_id=offer_id
    +        )
    +
    +    def _manage_sell_order(
    +        self,
    +        selling_asset: str,
    +        buying_asset: str,
    +        amount: Union[str, decimal.Decimal],
    +        price: Union[str, decimal.Decimal],
    +        timeout=30,
    +        offer_id=0,
    +    ):
    +        """Places/Deletes a selling order for amount `amount` of `selling_asset` for `buying_asset` with the price of `price`
    +
    +        Args:
    +            selling_asset (str): Selling Asset
    +            buying_asset str): Buying Asset
    +            amount (Union[str, decimal.Decimal]): Amount to sell.
    +            price (Union[str, decimal.Decimal]): Price for selling.
    +            timeout (int, optional): Timeout for submitting the transaction. Defaults to 30.
    +            offer_id: pass the current offer id and set the amount to 0 to cancel this offer or another amount to update the offer
    +
    +        Raises:
    +            ValueError: In case of invalid issuer.
    +            RuntimeError: Error happened during submission of the transaction.
    +
    +        Returns:
    +            (dict): response as the result of sumbit the transaction
    +        """
    +        stellar_selling_asset = self._get_asset(selling_asset)
    +        stellar_buying_asset = self._get_asset(buying_asset)
    +        server = self._get_horizon_server()
    +        tb = TransactionBuilder(self.load_account(), network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +        try:
    +            tx = (
    +                tb.append_manage_sell_offer_op(
    +                    selling_code=stellar_selling_asset.code,
    +                    selling_issuer=stellar_selling_asset.issuer,
    +                    buying_code=stellar_buying_asset.code,
    +                    buying_issuer=stellar_buying_asset.issuer,
    +                    amount=amount,
    +                    price=price,
    +                    offer_id=offer_id,
    +                )
    +                .set_timeout(timeout)
    +                .build()
    +            )
    +        except stellar_sdk.exceptions.AssetIssuerInvalidError as e:
    +            raise ValueError("invalid issuer") from e
    +        except Exception as e:
    +            raise RuntimeError(
    +                f"error while creating order for selling: {selling_asset}, buying: {buying_asset}, amount: {amount} price: {price}"
    +            ) from e
    +        else:
    +            tx.sign(self.secret)
    +            try:
    +                resp = server.submit_transaction(tx)
    +            except Exception as e:
    +                raise RuntimeError(
    +                    f"couldn't submit sell offer, probably wallet is unfunded. Please check the error stacktrace for more information."
    +                ) from e
    +            return resp
    +
    +    place_sell_order = _manage_sell_order
    +
    +    def get_created_offers(self, wallet_address: str = None):
    +        """Returns a list of the currently created offers
    +
    +        Args:
    +            wallet_address (Str, optional): wallet address you want to get offers to. Defaults to self.address.
    +
    +        Returns:
    +            list
    +        """
    +        wallet_address = wallet_address or self.address
    +        server = self._get_horizon_server()
    +        endpoint = server.offers()
    +        endpoint.for_account(wallet_address)
    +        response = endpoint.call()
    +        offers = response["_embedded"]["records"]
    +        return offers
    +
    +    def set_data_entry(self, name: str, value: str, address: str = None):
    +        """Sets, modifies or deletes a data entry (name/value pair) for an account
    +
    +        To delete a data entry, set the value to an empty string.
    +
    +        """
    +
    +        address = address or self.address
    +        signing_key = stellar_sdk.Keypair.from_secret(self.secret)
    +        horizon_server = self._get_horizon_server()
    +        if address == self.address:
    +            account = self.load_account()
    +        else:
    +            account = horizon_server.load_account(address)
    +        base_fee = horizon_server.fetch_base_fee()
    +        transaction = (
    +            stellar_sdk.TransactionBuilder(
    +                source_account=account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee
    +            )
    +            .append_manage_data_op(name, value)
    +            .set_timeout(30)
    +            .build()
    +        )
    +
    +        transaction.sign(signing_key)
    +
    +        try:
    +            response = horizon_server.submit_transaction(transaction)
    +            j.logger.info("Transaction hash: {}".format(response["hash"]))
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            j.logger.debug(e)
    +            raise e
    +
    +    def get_data_entries(self, address: str = None):
    +        address = address or self.address
    +        horizon_server = self._get_horizon_server()
    +        response = horizon_server.accounts().account_id(address).call()
    +        data = {}
    +        for data_name, data_value in response["data"].items():
    +            data[data_name] = base64.b64decode(data_value).decode("utf-8")
    +        return data
    +
    +    def merge_into_account(self, destination_address: str):
    +        """Merges XLMs into destination address
    +
    +        Args:
    +            destination_address (str): address to send XLMs to
    +        """
    +        server = self._get_horizon_server()
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +        source_account = self.load_account()
    +
    +        base_fee = server.fetch_base_fee()
    +
    +        transaction_builder = stellar_sdk.TransactionBuilder(
    +            source_account=source_account,
    +            network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +            base_fee=base_fee,
    +        )
    +
    +        balances = self.get_balance()
    +        for balance in balances.balances:
    +            if balance.is_native():
    +                continue
    +            # Step 1: Transfer custom assets
    +            if decimal.Decimal(balance.balance) > decimal.Decimal(0):
    +                transaction_builder.append_payment_op(
    +                    destination=destination_address,
    +                    amount=balance.balance,
    +                    asset_code=balance.asset_code,
    +                    asset_issuer=balance.asset_issuer,
    +                )
    +            # Step 2: Delete trustlines
    +            transaction_builder.append_change_trust_op(
    +                asset_issuer=balance.asset_issuer, asset_code=balance.asset_code, limit="0"
    +            )
    +
    +        transaction_builder.append_account_merge_op(destination=destination_address)
    +        transaction = transaction_builder.build()
    +
    +        transaction.sign(source_keypair)
    +        try:
    +            response = server.submit_transaction(transaction)
    +            j.logger.info("Transaction hash: {}".format(response["hash"]))
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            j.logger.debug(e)
    +
    +    def get_balance_by_asset(self, asset="TFT") -> float:
    +        balances = self.get_balance()
    +        for balance in balances.balances:
    +            if balance.asset_code == asset:
    +                return float(balance.balance)
    +        return 0
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Network +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
    +
    +

    An enumeration.

    +
    + +Expand source code + +
    class Network(Enum):
    +    STD = "STD"
    +    TEST = "TEST"
    +
    +

    Ancestors

    +
      +
    • enum.Enum
    • +
    +

    Class variables

    +
    +
    var STD
    +
    +
    +
    +
    var TEST
    +
    +
    +
    +
    +
    +
    +class Stellar +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class Stellar(Client):
    +    network = fields.Enum(Network)
    +    address = fields.String()
    +    sequence = fields.Integer()
    +    sequencedate = fields.Integer()
    +
    +    def secret_updated(self, value):
    +        self.address = stellar_sdk.Keypair.from_secret(value).public_key
    +
    +    secret = fields.String(on_update=secret_updated)
    +
    +    def _get_horizon_server(self):
    +        server_url = _HORIZON_NETWORKS[self.network.value]
    +        server = Server(horizon_url=server_url)
    +        return server
    +
    +    def _get_free_balances(self, address=None):
    +        address = address or self.address
    +        balances = AccountBalances(address)
    +        response = self._get_horizon_server().accounts().account_id(address).call()
    +        for response_balance in response["balances"]:
    +            balance = Balance.from_horizon_response(response_balance)
    +            if balance is not None:
    +                balances.add_balance(balance)
    +        return balances
    +
    +    def load_account(self):
    +        horizonServer = self._get_horizon_server()
    +        saccount = horizonServer.load_account(self.address)
    +        account = Account(saccount.account.account_id, saccount.sequence, self)
    +        return account
    +
    +    def _get_url(self, endpoint):
    +        url = _THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[self.network.value]
    +        if not j.sals.nettools.wait_connection_test(url, 443, 5):
    +            raise j.exceptions.Timeout(f"Can not connect to server {url}, connection timeout")
    +        endpoint = _THREEFOLDFOUNDATION_TFTSTELLAR_ENDPOINT[endpoint]
    +        return f"https://{url}{endpoint}"
    +
    +    def _fund_transaction(self, transaction):
    +        data = {"transaction": transaction}
    +        resp = j.tools.http.post(self._get_url("FUND"), json={"args": data})
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def _create_unlockhash_transaction(self, unlock_hash, transaction_xdr):
    +        data = {"unlockhash": unlock_hash, "transaction_xdr": transaction_xdr}
    +        resp = j.tools.http.post(self._get_url("CREATE_UNLOCK"), json={"args": data})
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def _get_unlockhash_transaction(self, unlockhash):
    +        data = {"unlockhash": unlockhash}
    +        resp = j.tools.http.post(self._get_url("GET_UNLOCK"), json={"args": data})
    +        if resp.status_code == j.tools.http.status_codes.codes.NOT_FOUND:
    +            return None
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def _create_activation_code(self):
    +        data = {"address": self.address}
    +        resp = j.tools.http.post(self._get_url("CREATE_ACTIVATION_CODE"), json={"args": data})
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def _activation_account(self):
    +        resp = j.tools.http.post(self._get_url("ACTIVATE_ACCOUNT"), json={"address": self.address})
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def set_unlock_transaction(self, unlock_transaction):
    +        """
    +        Adds a xdr encoded unlocktransaction
    +        :param unlock_transaction: xdr encoded unlocktransactionaddress of the destination.
    +        :type destination_address: str
    +        """
    +        txe = stellar_sdk.TransactionEnvelope.from_xdr(unlock_transaction, _NETWORK_PASSPHRASES[self.network.value])
    +        tx_hash = txe.hash()
    +        unlock_hash = stellar_sdk.strkey.StrKey.encode_pre_auth_tx(tx_hash)
    +
    +        self._create_unlockhash_transaction(unlock_hash=unlock_hash, transaction_xdr=txe.to_xdr())
    +
    +    def get_balance(self, address=None):
    +        """Gets the balances for a stellar address"""
    +        if address is None:
    +            address = self.address
    +        all_balances = self._get_free_balances(address)
    +        for account in self._find_escrow_accounts(address):
    +            all_balances.add_escrow_account(account)
    +        return all_balances
    +
    +    def _find_escrow_accounts(self, address=None):
    +        if address is None:
    +            address = self.address
    +        escrow_accounts = []
    +        accounts_endpoint = self._get_horizon_server().accounts()
    +        accounts_endpoint.for_signer(address)
    +        old_cursor = "old"
    +        new_cursor = ""
    +        while new_cursor != old_cursor:
    +            old_cursor = new_cursor
    +            accounts_endpoint.cursor(new_cursor)
    +            response = accounts_endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            cursor = parse.parse_qs(next_link_query).get("cursor")
    +            if not cursor:
    +                break
    +            new_cursor = cursor[0]
    +            accounts = response["_embedded"]["records"]
    +            for account in accounts:
    +                account_id = account["account_id"]
    +                if account_id == address:
    +                    continue  # Do not take the receiver's account
    +                all_signers = account["signers"]
    +                preauth_signers = [signer["key"] for signer in all_signers if signer["type"] == "preauth_tx"]
    +                # TODO check the tresholds and signers
    +                # TODO if we can merge, the amount is unlocked ( if len(preauth_signers))==0
    +                balances = []
    +                for response_balance in account["balances"]:
    +                    balances.append(Balance.from_horizon_response(response_balance))
    +                if is_vesting_account(
    +                    account,
    +                    address,
    +                    self.network.value,
    +                    _NETWORK_PASSPHRASES[self.network.value],
    +                    self._get_unlockhash_transaction,
    +                ):
    +                    escrow_accounts.append(VestingAccount(account_id, balances, VESTING_SCHEME))
    +                else:
    +                    escrow_accounts.append(
    +                        EscrowAccount(
    +                            account_id,
    +                            preauth_signers,
    +                            balances,
    +                            _NETWORK_PASSPHRASES[self.network.value],
    +                            self._get_unlockhash_transaction,
    +                        )
    +                    )
    +        return escrow_accounts
    +
    +    def claim_locked_funds(self):
    +        balances = self.get_balance()
    +        for locked_account in balances.escrow_accounts:
    +            if locked_account.can_be_unlocked():
    +                self._unlock_account(locked_account)
    +
    +    def _unlock_account(self, escrow_account):
    +        submitted_unlock_transactions = 0
    +        for unlockhash in escrow_account.unlockhashes:
    +            unlockhash_transation = self._get_unlockhash_transaction(unlockhash=unlockhash)
    +            if unlockhash_transation is None:
    +                return
    +            j.logger.info(unlockhash_transation["transaction_xdr"])
    +            self._get_horizon_server().submit_transaction(unlockhash_transation["transaction_xdr"])
    +            submitted_unlock_transactions += 1
    +
    +        if submitted_unlock_transactions == len(escrow_account.unlockhashes):
    +            self._merge_account(escrow_account.address)
    +
    +    def _merge_account(self, address):
    +        server = self._get_horizon_server()
    +        account = server.load_account(address)
    +        # Increment the sequence number in case the unlock transaction was not processed before the load_account call
    +        # account.increment_sequence_number()
    +        balances = self._get_free_balances(address)
    +        base_fee = server.fetch_base_fee()
    +        transaction_builder = stellar_sdk.TransactionBuilder(
    +            source_account=account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee
    +        )
    +        for balance in balances.balances:
    +            if balance.is_native():
    +                continue
    +            # Step 1: Transfer custom assets
    +            if decimal.Decimal(balance.balance) > decimal.Decimal(0):
    +                transaction_builder.append_payment_op(
    +                    destination=self.address,
    +                    amount=balance.balance,
    +                    asset_code=balance.asset_code,
    +                    asset_issuer=balance.asset_issuer,
    +                    source=account.account.account_id,
    +                )
    +            # Step 2: Delete trustlines
    +            transaction_builder.append_change_trust_op(
    +                asset_issuer=balance.asset_issuer, asset_code=balance.asset_code, limit="0"
    +            )
    +        # Step 3: Merge account
    +        transaction_builder.append_account_merge_op(self.address)
    +
    +        transaction_builder.set_timeout(30)
    +        transaction = transaction_builder.build()
    +        signer_kp = stellar_sdk.Keypair.from_secret(self.secret)
    +        transaction.sign(signer_kp)
    +        server.submit_transaction(transaction)
    +
    +    def activate_through_friendbot(self):
    +        """Activates and funds a testnet account using friendbot"""
    +        if self.network.value != "TEST":
    +            raise Exception("Account activation through friendbot is only available on testnet")
    +
    +        resp = j.tools.http.get("https://friendbot.stellar.org", params={"addr": self.address})
    +        resp.raise_for_status()
    +        j.logger.info(f"account with address {self.address} activated and funded through friendbot")
    +
    +    def activate_through_threefold_service(self):
    +        """
    +        Activate your wallet through threefold services
    +        """
    +        for _ in range(5):
    +            j.logger.info(f"trying to activate : {self.instance_name}")
    +            try:
    +                resp = self._activation_account()
    +                loaded_json = j.data.serializers.json.loads(resp)
    +                xdr = loaded_json["activation_transaction"]
    +                self.sign(xdr, submit=True)
    +                j.logger.info(f"{self.instance_name} is activated using the activation service.")
    +                return
    +            except Exception as e:
    +                j.logger.error(f"failed to activate using the activation service {e}")
    +                ## Try activating with `activation_wallet` j.clients.stellar.activation_wallet if exists
    +                ## this activator should be imported on the system.
    +        else:
    +            raise RuntimeError(
    +                "could not activate wallet. tried activation service and there's no activation_wallet configured on the system"
    +            )
    +
    +    def activate_through_activation_wallet(self, wallet_name="activation_wallet"):
    +        """Activate your wallet through activation wallet."""
    +        if wallet_name in j.clients.stellar.list_all() and self.instance_name != wallet_name:
    +            j.logger.info(f"trying to fund the wallet ourselves with the activation wallet")
    +            j.logger.info(f"activation wallet {self.instance_name}")
    +            for _ in range(5):
    +                try:
    +                    j.clients.stellar.activation_wallet.activate_account(self.address, "2.6")
    +                    self.add_known_trustline("TFT")
    +                    j.logger.info(f"activated wallet {self.instance_name}")
    +                    return
    +                except Exception as e:
    +                    j.logger.error(f"failed to activate wallet {self.instance_name} using activation_wallet")
    +        else:
    +            raise RuntimeError(f"could not find the activation wallet: {wallet_name}")
    +
    +    def activate_account(self, destination_address, starting_balance="3.6"):
    +        """Activates another account
    +
    +        Args:
    +            destination_address (str): address of the destination
    +            starting_balance (str, optional): the balance that the destination address will start with. Must be a positive integer expressed as a string. Defaults to "12.50".
    +        """
    +        server = self._get_horizon_server()
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +        source_account = self.load_account()
    +
    +        base_fee = server.fetch_base_fee()
    +        transaction = (
    +            stellar_sdk.TransactionBuilder(
    +                source_account=source_account,
    +                network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +                base_fee=base_fee,
    +            )
    +            .append_create_account_op(destination=destination_address, starting_balance=starting_balance)
    +            .build()
    +        )
    +        transaction.sign(source_keypair)
    +        try:
    +            response = server.submit_transaction(transaction)
    +            j.logger.info("Transaction hash: {}".format(response["hash"]))
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            j.logger.debug(e)
    +
    +    def add_trustline(self, asset_code, issuer, secret=None):
    +        """Create a trustline to an asset
    +
    +        Args:
    +            asset_code (str): code of the asset. For example: 'BTC', 'TFT', ...
    +            issuer (str): address of the asset issuer
    +            secret (str, optional): Secret to use will use instance property if empty. Defaults to None.
    +        """
    +        self._change_trustline(asset_code, issuer, secret=secret)
    +        j.logger.info(f"Added trustline {asset_code}:{issuer} to account {self.address}")
    +
    +    def add_known_trustline(self, asset_code):
    +        """Will add a trustline known by threefold for chosen asset_code
    +
    +        Args:
    +            asset_code (str): code of the asset. For example: 'BTC', 'TFT', ...
    +        """
    +        j.logger.info(f"adding trustline {asset_code} to account {self.address}")
    +        balances = self.get_balance()
    +        for b in balances.balances:
    +            if b.asset_code == asset_code:
    +                j.logger.info(f"trustline {asset_code} is already added.")
    +                return
    +        issuer = _NETWORK_KNOWN_TRUSTS.get(self.network.value, {}).get(asset_code)
    +        if not issuer:
    +            raise j.exceptions.NotFound(f"There is no known issuer for {asset_code} on network {self.network}")
    +        self._change_trustline(asset_code, issuer)
    +
    +    def delete_trustline(self, asset_code, issuer, secret=None):
    +        """Deletes a trustline
    +
    +        Args:
    +            asset_code (str): code of the asset. For example: 'BTC', 'XRP', ...
    +            issuer (str): address of the asset issuer
    +            secret (str, optional): Secret to use will use instance property if empty. Defaults to None.
    +        """
    +        self._change_trustline(asset_code, issuer, limit="0", secret=secret)
    +        j.logger.info(f"Removed trustline {asset_code}:{issuer} from account {self.address}")
    +
    +    def _change_trustline(self, asset_code, issuer, limit=None, secret=None):
    +        """Create a trustline between you and the issuer of an asset
    +
    +        Args:
    +            asset_code (str): code which form the asset. For example: 'BTC', 'TFT', ...
    +            issuer (str): address of the asset issuer
    +            limit ([type], optional): The limit for the asset, defaults to max int64(922337203685.4775807). If the limit is set to “0” it deletes the trustline. Defaults to None.
    +            secret (str, optional): Secret to use will use instance property if empty. Defaults to None.
    +        """
    +        # if no secret is provided we assume we change trustlines for this account
    +        secret = secret or self.secret
    +
    +        server = self._get_horizon_server()
    +        source_keypair = stellar_sdk.Keypair.from_secret(secret)
    +        source_public_key = source_keypair.public_key
    +        source_account = server.load_account(source_public_key)
    +
    +        base_fee = server.fetch_base_fee()
    +        transaction = (
    +            stellar_sdk.TransactionBuilder(
    +                source_account=source_account,
    +                network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +                base_fee=base_fee,
    +            )
    +            .append_change_trust_op(Asset(asset_code,issuer), limit=limit)
    +            .set_timeout(30)
    +            .build()
    +        )
    +
    +        transaction.sign(source_keypair)
    +
    +        try:
    +            server.submit_transaction(transaction)
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            j.logger.debug(e)
    +            raise e
    +
    +    def return_xlms_to_activation(self):
    +        xlm_balance = 0
    +        for balance in self.get_balances():
    +            if balance.asset_code == "XLM":
    +                xlm_balance = balance.balance
    +        trustlines = len(self.get_balances()) - 1
    +        minimum_balance = 1 + 0.5 * trustlines
    +        amount = xlm_balance - minimum_balance - XLM_TRANSACTION_FEES
    +        self.transfer(ACTIVATION_ADDRESS, amount)
    +
    +    def transfer(
    +        self,
    +        destination_address,
    +        amount,
    +        asset="XLM",
    +        locked_until=None,
    +        memo_text=None,
    +        memo_hash=None,
    +        fund_transaction=True,
    +        from_address=None,
    +        timeout=30,
    +        sequence_number: int = None,
    +        sign: bool = True,
    +        retries: int = 5,
    +    ):
    +        """Transfer assets to another address
    +
    +        Args:
    +            destination_address (str): address of the destination
    +            amount (str): can be a floating point number with 7 numbers after the decimal point expressed as a string
    +            asset (str, optional): asset to transfer. Defaults to "XLM". if you wish to specify an asset it should be in format 'assetcode:issuer'. Where issuer is the address of the
    +            issuer of the asset.
    +            locked_until (float, optional): epoch timestamp indicating until when the tokens  should be locked. Defaults to None.
    +            memo_text (Union[str, bytes], optional): memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long. Defaults to None.
    +            memo_hash (Union[str, bytes], optional): memo hash to add to the transaction, A 32 byte hash. Defaults to None.
    +            fund_transaction (bool, optional): use the threefoldfoundation transaction funding service. Defautls to True.
    +            from_address (str, optional): Use a different address to send the tokens from, useful in multisig use cases. Defaults to None.
    +            timeout (int,optional: Seconds from now on until when the transaction to be submitted to the stellar network
    +            sequence_number (int,optional): specify a specific sequence number ( will still be increased by one) instead of loading it from the account
    +            sign (bool,optional) : Do not sign and submit the transaction
    +
    +        Raises:
    +            Exception: If asset not in correct format
    +            stellar_sdk.exceptions.BadRequestError: not enough funds for opertaion
    +            stellar_sdk.exceptions.BadRequestError: bad transfer authentication
    +
    +        Returns:
    +            [type]: [description]
    +        """
    +        if decimal.Decimal(amount) <= 0:
    +            j.logger.warning("Can not transfer empty or zero amount transaction")
    +            return
    +        nretries = 0
    +        while nretries < retries:
    +            try:
    +                return self._transfer(
    +                    destination_address=destination_address,
    +                    amount=amount,
    +                    asset=asset,
    +                    locked_until=locked_until,
    +                    memo_text=memo_text,
    +                    memo_hash=memo_hash,
    +                    fund_transaction=fund_transaction,
    +                    from_address=from_address,
    +                    timeout=timeout,
    +                    sequence_number=sequence_number,
    +                    sign=sign,
    +                )
    +            except Exception as e:
    +                nretries += 1
    +                gevent.sleep(1)
    +                j.logger.warning(str(e))
    +
    +        raise j.exceptions.Runtime(f"Failed to make transaction for {retries} times, Please try again later")
    +
    +    def _transfer(
    +        self,
    +        destination_address,
    +        amount,
    +        asset="XLM",
    +        locked_until=None,
    +        memo_text=None,
    +        memo_hash=None,
    +        fund_transaction=True,
    +        from_address=None,
    +        timeout=30,
    +        sequence_number: int = None,
    +        sign: bool = True,
    +    ):
    +        issuer = None
    +        j.logger.info(f"Sending {amount} {asset} from {self.address} to {destination_address}")
    +        if asset != "XLM":
    +            assetStr = asset.split(":")
    +            if len(assetStr) != 2:
    +                raise Exception(f"Wrong asset format should be in format 'assetcode:issuer', but received {assetStr}")
    +            asset_code = assetStr[0]
    +            issuer = assetStr[1]
    +        else:
    +            asset_code = asset
    +
    +        if locked_until is not None:
    +            return self._transfer_locked_tokens(
    +                destination_address,
    +                amount,
    +                asset_code,
    +                issuer,
    +                locked_until,
    +                memo_text=memo_text,
    +                memo_hash=memo_hash,
    +                fund_transaction=fund_transaction,
    +                from_address=from_address,
    +            )
    +
    +        horizon_server = self._get_horizon_server()
    +
    +        base_fee = horizon_server.fetch_base_fee()
    +        if from_address:
    +            source_account = horizon_server.load_account(from_address)
    +        else:
    +            source_account = self.load_account()
    +
    +        if sequence_number:
    +            source_account.sequence = sequence_number
    +
    +        transaction_builder = stellar_sdk.TransactionBuilder(
    +            source_account=source_account,
    +            network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +            base_fee=base_fee,
    +        )
    +        transaction_builder.append_payment_op(
    +            destination=destination_address,
    +            amount=str(amount),
    +            asset=self._get_asset(asset_code),
    +            source=source_account.account.account_id,
    +        )
    +        transaction_builder.set_timeout(timeout)
    +        if memo_text is not None:
    +            transaction_builder.add_text_memo(memo_text)
    +        if memo_hash is not None:
    +            transaction_builder.add_hash_memo(memo_hash)
    +
    +        transaction = transaction_builder.build()
    +        transaction = transaction.to_xdr()
    +
    +        if asset_code in _NETWORK_KNOWN_TRUSTS[self.network.value]:
    +            if fund_transaction:
    +                transaction = self._fund_transaction(transaction=transaction)
    +                transaction = transaction["transaction_xdr"]
    +
    +        if not sign:
    +            return transaction
    +
    +        transaction = stellar_sdk.TransactionEnvelope.from_xdr(transaction, _NETWORK_PASSPHRASES[self.network.value])
    +
    +        my_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +        transaction.sign(my_keypair)
    +
    +        response = horizon_server.submit_transaction(transaction)
    +        tx_hash = response["hash"]
    +        j.logger.info(f"Transaction hash: {tx_hash}")
    +        return tx_hash
    +
    +    def list_payments(self, address: str = None, asset: str = None, cursor: str = None):
    +        """Get the transactions for an adddress
    +        :param address: address of the effects.In None, the address of this wallet is taken
    +        :param asset: stellar asset in the code:issuer form( except for XLM, which does not need an issuer)
    +        :param cursor:pass a cursor to continue after the last call or an empty str to start receivibg a cursor
    +         if a cursor is passed, a tuple of the payments and the cursor is returned
    +        """
    +        if address is None:
    +            address = self.address
    +        tx_endpoint = self._get_horizon_server().payments()
    +        tx_endpoint.for_account(address)
    +        tx_endpoint.limit(50)
    +        payments = []
    +        old_cursor = "old"
    +        new_cursor = ""
    +        if cursor is not None:
    +            new_cursor = cursor
    +        while old_cursor != new_cursor:
    +            old_cursor = new_cursor
    +            tx_endpoint.cursor(new_cursor)
    +            response = tx_endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +            response_payments = response["_embedded"]["records"]
    +            for response_payment in response_payments:
    +                ps = PaymentSummary.from_horizon_response(response_payment, address)
    +                if asset:
    +                    split_asset = asset.split(":")
    +                    assetcode = split_asset[0]
    +                    assetissuer = None
    +                    if len(split_asset) > 1:
    +                        assetissuer = split_asset[1]
    +                    if ps.balance and ps.balance.asset_code == assetcode:
    +                        if assetissuer and assetissuer == ps.balance.asset_issuer:
    +                            payments.append(ps)
    +                else:
    +                    payments.append(ps)
    +        if cursor is not None:
    +            return {"payments": payments, "cursor": new_cursor}
    +
    +        return payments
    +
    +    def list_transactions(self, address: str = None, cursor: str = None):
    +        """Get the transactions for an adddres
    +        :param address (str, optional): address of the effects.If None, the address of this wallet is taken. Defaults to None.
    +        :param cursor:pass a cursor to continue after the last call or an empty str to start receivibg a cursor
    +         if a cursor is passed, a tuple of the payments and the cursor is returned
    +
    +        Returns:
    +            list: list of TransactionSummary objects
    +            dictionary: {"transactions":list of TransactionSummary objects, "cursor":cursor}
    +        """
    +        address = address or self.address
    +        tx_endpoint = self._get_horizon_server().transactions()
    +        tx_endpoint.for_account(address)
    +        tx_endpoint.include_failed(True)
    +        transactions = []
    +        old_cursor = "old"
    +        new_cursor = ""
    +        if cursor is not None:
    +            new_cursor = cursor
    +        while old_cursor != new_cursor:
    +            old_cursor = new_cursor
    +            tx_endpoint.cursor(new_cursor)
    +            response = tx_endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +            response_transactions = response["_embedded"]["records"]
    +            for response_transaction in response_transactions:
    +                if response_transaction["successful"]:
    +                    transactions.append(TransactionSummary.from_horizon_response(response_transaction))
    +
    +        if cursor is not None:
    +            return {"transactions": transactions, "cursor": new_cursor}
    +        return transactions
    +
    +    def get_transaction_effects(self, transaction_hash, address=None):
    +        """Get the effects on an adddressfor a specific transaction
    +
    +        Args:
    +            transaction_hash (str): hash of the transaction
    +            address (str, optional): address of the effects.If None, the address of this wallet is taken. Defaults to None.
    +
    +        Returns:
    +            list: list of Effect objects
    +        """
    +        address = address or self.address
    +        effects = []
    +        endpoint = self._get_horizon_server().effects()
    +        endpoint.for_transaction(transaction_hash)
    +        old_cursor = "old"
    +        new_cursor = ""
    +        while old_cursor != new_cursor:
    +            old_cursor = new_cursor
    +            endpoint.cursor(new_cursor)
    +            response = endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +            response_effects = response["_embedded"]["records"]
    +            for response_effect in response_effects:
    +                if "account" in response_effect and response_effect["account"] == address:
    +                    effects.append(Effect.from_horizon_response(response_effect))
    +        return effects
    +
    +    def _transfer_locked_tokens(
    +        self,
    +        destination_address,
    +        amount,
    +        asset_code,
    +        asset_issuer,
    +        unlock_time,
    +        memo_text=None,
    +        memo_hash=None,
    +        fund_transaction=True,
    +        from_address=None,
    +    ):
    +        """Transfer locked assets to another address
    +
    +        Args:
    +            destination_address (str): address of the destination
    +            amount (str): amount, can be a floating point number with 7 numbers after the decimal point expressed as a string
    +            asset_code (str): asset to transfer
    +            asset_issuer (str): if the asset_code is different from 'XlM', the issuer address
    +            unlock_time (float):  an epoch timestamp indicating when the funds should be unlocked
    +            memo_text (Union[str, bytes], optional): memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long
    +            memo_hash (Union[str, bytes], optional): memo hash to add to the transaction, A 32 byte hash
    +            fund_transaction (bool, optional): use the threefoldfoundation transaction funding service.Defaults to True.
    +            from_address (str, optional): Use a different address to send the tokens from, useful in multisig use cases. Defaults to None.
    +
    +        Returns:
    +            [type]: [description]
    +        """
    +
    +        unlock_time = math.ceil(unlock_time)
    +
    +        j.logger.info("Creating escrow account")
    +        escrow_kp = stellar_sdk.Keypair.random()
    +
    +        # minimum account balance as described at https://www.stellar.org/developers/guides/concepts/fees.html#minimum-account-balance
    +        horizon_server = self._get_horizon_server()
    +        base_fee = horizon_server.fetch_base_fee()
    +        base_reserve = 0.5
    +        minimum_account_balance = (2 + 1 + 3) * base_reserve  # 1 trustline and 3 signers
    +        required_XLM = minimum_account_balance + base_fee * 0.0000001 * 3
    +
    +        j.logger.info("Activating escrow account")
    +        self.activate_account(escrow_kp.public_key, str(math.ceil(required_XLM)))
    +
    +        if asset_code != "XLM":
    +            j.logger.info("Adding trustline to escrow account")
    +            self.add_trustline(asset_code, asset_issuer, escrow_kp.secret)
    +
    +        preauth_tx = self._create_unlock_transaction(escrow_kp, unlock_time)
    +        preauth_tx_hash = preauth_tx.hash()
    +
    +        # save the preauth transaction in our unlock service
    +        unlock_hash = stellar_sdk.strkey.StrKey.encode_pre_auth_tx(preauth_tx_hash)
    +        self._create_unlockhash_transaction(unlock_hash=unlock_hash, transaction_xdr=preauth_tx.to_xdr())
    +
    +        self._set_account_signers(escrow_kp.public_key, destination_address, preauth_tx_hash, escrow_kp)
    +        j.logger.info(preauth_tx.to_xdr())
    +
    +        self.transfer(
    +            escrow_kp.public_key,
    +            amount,
    +            asset_code + ":" + asset_issuer,
    +            memo_text=memo_text,
    +            memo_hash=memo_hash,
    +            fund_transaction=fund_transaction,
    +            from_address=from_address,
    +        )
    +        return preauth_tx.to_xdr()
    +
    +    def _create_unlock_transaction(self, escrow_kp, unlock_time):
    +        server = self._get_horizon_server()
    +        escrow_account = server.load_account(escrow_kp.public_key)
    +        escrow_account.increment_sequence_number()
    +        tx = (
    +            stellar_sdk.TransactionBuilder(escrow_account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +            .append_set_options_op(master_weight=0, low_threshold=1, med_threshold=1, high_threshold=1)
    +            .add_time_bounds(unlock_time, 0)
    +            .build()
    +        )
    +        tx.sign(escrow_kp)
    +        return tx
    +
    +    def _set_account_signers(self, address, public_key_signer, preauth_tx_hash, signer_kp):
    +        server = self._get_horizon_server()
    +        if address == self.address:
    +            account = self.load_account()
    +        else:
    +            account = server.load_account(address)
    +        tx = (
    +            stellar_sdk.TransactionBuilder(account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +            .append_pre_auth_tx_signer(preauth_tx_hash, 1)
    +            .append_ed25519_public_key_signer(public_key_signer, 1)
    +            .append_set_options_op(master_weight=1, low_threshold=2, med_threshold=2, high_threshold=2)
    +            .build()
    +        )
    +
    +        tx.sign(signer_kp)
    +        response = server.submit_transaction(tx)
    +        j.logger.info(response)
    +        j.logger.info(f"Set the signers of {address} to {public_key_signer} and {preauth_tx_hash}")
    +
    +    def get_signing_requirements(self, address: str = None):
    +        address = address or self.address
    +        response = self._get_horizon_server().accounts().account_id(address).call()
    +        signing_requirements = {}
    +        signing_requirements["thresholds"] = response["thresholds"]
    +        signing_requirements["signers"] = response["signers"]
    +        return signing_requirements
    +
    +    def modify_signing_requirements(
    +        self, public_keys_signers, signature_count, low_treshold=0, high_treshold=2, master_weight=2
    +    ):
    +        """modify_signing_requirements sets to amount of signatures required for the creation of multisig account. It also adds
    +           the public keys of the signer to this account
    +
    +        Args:
    +            public_keys_signers (list): list of public keys of signers
    +            signature_count (int): amount of signatures requires to transfer funds
    +            low_treshold (int, optional): amount of signatures required for low security operations (transaction processing, allow trust, bump sequence). Defaults to 1.
    +            high_treshold (int, optional): amount of signatures required for high security operations (set options, account merge). Defaults to 2.
    +            master_weight (int, optional): A number from 0-255 (inclusive) representing the weight of the master key. If the weight of the master key is updated to 0, it is effectively disabled. Defaults to 2.
    +        """
    +        server = self._get_horizon_server()
    +        account = self.load_account()
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +        transaction_builder = stellar_sdk.TransactionBuilder(
    +            account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value]
    +        )
    +        # set the signing options
    +        transaction_builder.append_set_options_op(
    +            low_threshold=low_treshold,
    +            med_threshold=signature_count,
    +            high_threshold=high_treshold,
    +            master_weight=master_weight,
    +        )
    +
    +        # For every public key given, add it as a signer to this account
    +        for public_key_signer in public_keys_signers:
    +            transaction_builder.append_ed25519_public_key_signer(public_key_signer, 1)
    +
    +        transaction_builder.set_timeout(30)
    +        tx = transaction_builder.build()
    +        tx.sign(source_keypair)
    +
    +        try:
    +            response = server.submit_transaction(tx)
    +            j.logger.info(response)
    +            j.logger.info(f"Set the signers of {self.address} to require {signature_count} signers")
    +        except stellar_sdk.exceptions.BadRequestError:
    +            j.logger.info("Transaction need additional signatures in order to send")
    +            return tx.to_xdr()
    +
    +    def sign(self, tx_xdr: str, submit: bool = True):
    +        """sign signs a transaction xdr and optionally submits it to the network
    +
    +        Args:
    +            tx_xdr (str): transaction to sign in xdr format
    +            submit (bool,optional): submit the transaction tro the Stellar network
    +        """
    +
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +        tx = stellar_sdk.TransactionEnvelope.from_xdr(tx_xdr, _NETWORK_PASSPHRASES[self.network.value])
    +        tx.sign(source_keypair)
    +        if submit:
    +            horizon_server = self._get_horizon_server()
    +            horizon_server.submit_transaction(tx)
    +        else:
    +            return tx.to_xdr()
    +
    +    def sign_multisig_transaction(self, tx_xdr):
    +        """sign_multisig_transaction signs a transaction xdr and tries to submit it to the network
    +
    +           Deprecated, use sign instead
    +
    +        Args:
    +            tx_xdr (str): transaction to sign in xdr format
    +        """
    +
    +        try:
    +            self.sign(tx_xdr)
    +            j.logger.info("Multisig tx signed and sent")
    +        except UnAuthorized as e:
    +            j.logger.info("Transaction needs additional signatures in order to send")
    +            return e.transaction_xdr
    +
    +    def remove_signer(self, public_key_signer):
    +        """remove_signer removes a public key as a signer from the source account
    +
    +        Args:
    +            public_key_signer (str): public key of an account
    +        """
    +        server = self._get_horizon_server()
    +        account = self.load_account()
    +        tx = (
    +            stellar_sdk.TransactionBuilder(account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +            .append_ed25519_public_key_signer(public_key_signer, 0)
    +            .build()
    +        )
    +
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +        tx.sign(source_keypair)
    +        try:
    +            response = server.submit_transaction(tx)
    +            j.logger.info(response)
    +            j.logger.info("Multisig tx signed and sent")
    +        except stellar_sdk.exceptions.BadRequestError:
    +            j.logger.info("Transaction need additional signatures in order to send")
    +            return tx.to_xdr()
    +
    +    def get_sender_wallet_address(self, transaction_hash):
    +        """Get the sender's wallet address from a transaction hash
    +
    +        Args:
    +            transaction_hash (String): Transaction hash
    +
    +        Returns:
    +            String : Wallet Hash
    +
    +        """
    +        server = self._get_horizon_server()
    +        endpoint = server.operations().for_transaction(transaction_hash)
    +        response = endpoint.call()
    +        # not possible for a transaction to have more than a source, so will take first one
    +        wallet_address = response["_embedded"]["records"][0]["source_account"]
    +        return wallet_address
    +
    +    def check_is_payment_transaction(self, transaction_hash):
    +        """Some transactions doesn't have an amount like activating the wallet
    +        This helper method to help in iterating in transactions
    +
    +        Args:
    +            transaction_hash (String): Transaction hash
    +
    +        Returns:
    +            Bool: True if transaction has amount - False if not
    +        """
    +
    +        server = self._get_horizon_server()
    +        endpoint = server.operations().for_transaction(transaction_hash)
    +        response = endpoint.call()
    +        results = response["_embedded"]["records"][0]
    +        return results["type"] == "payment"
    +
    +    def _get_asset(self, code="TFT", issuer=None) -> stellar_sdk.Asset:
    +        """Gets an stellar_sdk.Asset object by code.
    +        if the code is TFT or TFTA we quickly return the Asset object based on the code.
    +        if the code is native (XLM) we return the Asset object with None issuer.
    +        if the code isn't unknown, exception is raised to manually construct the Asset object.
    +
    +        Args:
    +            code (str, optional): code for the asset. Defaults to "TFT".
    +            issuer (str, optional): issuer for the asset. Defaults to None.
    +
    +        Raises:
    +            ValueError: empty code, In case of issuer is None and not XLM or the code isn't for TFT or TFTA.
    +            stellar_sdk.exceptions.AssetIssuerInvalidError: Invalid issuer
    +        Returns:
    +            stellar_sdk.Asset: Asset object.
    +        """
    +        network = self.network.value
    +        KNOWN_ASSETS = list(_NETWORK_KNOWN_TRUSTS[network].keys()) + ["XLM"]
    +
    +        if issuer and code:
    +            return Asset(code, issuer)
    +
    +        if not code:
    +            raise ValueError("An asset code is required")
    +
    +        if not issuer and code not in KNOWN_ASSETS:
    +            raise ValueError(
    +                f"Make sure to supply the issuer for {code}, issuer is allowed to be none only in case of {KNOWN_ASSETS}"
    +            )
    +
    +        if not issuer and code in KNOWN_ASSETS:
    +            asset_issuer = _NETWORK_KNOWN_TRUSTS[network].get(code, None)
    +            return Asset(code, asset_issuer)
    +
    +    def cancel_sell_order(self, offer_id, selling_asset: str, buying_asset: str, price: Union[str, decimal.Decimal]):
    +        """Deletes a selling order for amount `amount` of `selling_asset` for `buying_asset` with the price of `price`
    +
    +        Args:
    +            selling_asset (str): Selling Asset
    +            buying_asset (str): Buying Asset
    +            offer_id (int): pass the current offer id and set the amount to 0 to cancel this offer
    +            price (str): order price
    +        """
    +        return self._manage_sell_order(
    +            selling_asset=selling_asset, buying_asset=buying_asset, amount="0", price=price, offer_id=offer_id
    +        )
    +
    +    def _manage_sell_order(
    +        self,
    +        selling_asset: str,
    +        buying_asset: str,
    +        amount: Union[str, decimal.Decimal],
    +        price: Union[str, decimal.Decimal],
    +        timeout=30,
    +        offer_id=0,
    +    ):
    +        """Places/Deletes a selling order for amount `amount` of `selling_asset` for `buying_asset` with the price of `price`
    +
    +        Args:
    +            selling_asset (str): Selling Asset
    +            buying_asset str): Buying Asset
    +            amount (Union[str, decimal.Decimal]): Amount to sell.
    +            price (Union[str, decimal.Decimal]): Price for selling.
    +            timeout (int, optional): Timeout for submitting the transaction. Defaults to 30.
    +            offer_id: pass the current offer id and set the amount to 0 to cancel this offer or another amount to update the offer
    +
    +        Raises:
    +            ValueError: In case of invalid issuer.
    +            RuntimeError: Error happened during submission of the transaction.
    +
    +        Returns:
    +            (dict): response as the result of sumbit the transaction
    +        """
    +        stellar_selling_asset = self._get_asset(selling_asset)
    +        stellar_buying_asset = self._get_asset(buying_asset)
    +        server = self._get_horizon_server()
    +        tb = TransactionBuilder(self.load_account(), network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +        try:
    +            tx = (
    +                tb.append_manage_sell_offer_op(
    +                    selling_code=stellar_selling_asset.code,
    +                    selling_issuer=stellar_selling_asset.issuer,
    +                    buying_code=stellar_buying_asset.code,
    +                    buying_issuer=stellar_buying_asset.issuer,
    +                    amount=amount,
    +                    price=price,
    +                    offer_id=offer_id,
    +                )
    +                .set_timeout(timeout)
    +                .build()
    +            )
    +        except stellar_sdk.exceptions.AssetIssuerInvalidError as e:
    +            raise ValueError("invalid issuer") from e
    +        except Exception as e:
    +            raise RuntimeError(
    +                f"error while creating order for selling: {selling_asset}, buying: {buying_asset}, amount: {amount} price: {price}"
    +            ) from e
    +        else:
    +            tx.sign(self.secret)
    +            try:
    +                resp = server.submit_transaction(tx)
    +            except Exception as e:
    +                raise RuntimeError(
    +                    f"couldn't submit sell offer, probably wallet is unfunded. Please check the error stacktrace for more information."
    +                ) from e
    +            return resp
    +
    +    place_sell_order = _manage_sell_order
    +
    +    def get_created_offers(self, wallet_address: str = None):
    +        """Returns a list of the currently created offers
    +
    +        Args:
    +            wallet_address (Str, optional): wallet address you want to get offers to. Defaults to self.address.
    +
    +        Returns:
    +            list
    +        """
    +        wallet_address = wallet_address or self.address
    +        server = self._get_horizon_server()
    +        endpoint = server.offers()
    +        endpoint.for_account(wallet_address)
    +        response = endpoint.call()
    +        offers = response["_embedded"]["records"]
    +        return offers
    +
    +    def set_data_entry(self, name: str, value: str, address: str = None):
    +        """Sets, modifies or deletes a data entry (name/value pair) for an account
    +
    +        To delete a data entry, set the value to an empty string.
    +
    +        """
    +
    +        address = address or self.address
    +        signing_key = stellar_sdk.Keypair.from_secret(self.secret)
    +        horizon_server = self._get_horizon_server()
    +        if address == self.address:
    +            account = self.load_account()
    +        else:
    +            account = horizon_server.load_account(address)
    +        base_fee = horizon_server.fetch_base_fee()
    +        transaction = (
    +            stellar_sdk.TransactionBuilder(
    +                source_account=account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee
    +            )
    +            .append_manage_data_op(name, value)
    +            .set_timeout(30)
    +            .build()
    +        )
    +
    +        transaction.sign(signing_key)
    +
    +        try:
    +            response = horizon_server.submit_transaction(transaction)
    +            j.logger.info("Transaction hash: {}".format(response["hash"]))
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            j.logger.debug(e)
    +            raise e
    +
    +    def get_data_entries(self, address: str = None):
    +        address = address or self.address
    +        horizon_server = self._get_horizon_server()
    +        response = horizon_server.accounts().account_id(address).call()
    +        data = {}
    +        for data_name, data_value in response["data"].items():
    +            data[data_name] = base64.b64decode(data_value).decode("utf-8")
    +        return data
    +
    +    def merge_into_account(self, destination_address: str):
    +        """Merges XLMs into destination address
    +
    +        Args:
    +            destination_address (str): address to send XLMs to
    +        """
    +        server = self._get_horizon_server()
    +        source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +        source_account = self.load_account()
    +
    +        base_fee = server.fetch_base_fee()
    +
    +        transaction_builder = stellar_sdk.TransactionBuilder(
    +            source_account=source_account,
    +            network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +            base_fee=base_fee,
    +        )
    +
    +        balances = self.get_balance()
    +        for balance in balances.balances:
    +            if balance.is_native():
    +                continue
    +            # Step 1: Transfer custom assets
    +            if decimal.Decimal(balance.balance) > decimal.Decimal(0):
    +                transaction_builder.append_payment_op(
    +                    destination=destination_address,
    +                    amount=balance.balance,
    +                    asset_code=balance.asset_code,
    +                    asset_issuer=balance.asset_issuer,
    +                )
    +            # Step 2: Delete trustlines
    +            transaction_builder.append_change_trust_op(
    +                asset_issuer=balance.asset_issuer, asset_code=balance.asset_code, limit="0"
    +            )
    +
    +        transaction_builder.append_account_merge_op(destination=destination_address)
    +        transaction = transaction_builder.build()
    +
    +        transaction.sign(source_keypair)
    +        try:
    +            response = server.submit_transaction(transaction)
    +            j.logger.info("Transaction hash: {}".format(response["hash"]))
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            j.logger.debug(e)
    +
    +    def get_balance_by_asset(self, asset="TFT") -> float:
    +        balances = self.get_balance()
    +        for balance in balances.balances:
    +            if balance.asset_code == asset:
    +                return float(balance.balance)
    +        return 0
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var address
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var network
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var secret
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var sequence
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var sequencedate
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def activate_account(self, destination_address, starting_balance='3.6') +
    +
    +

    Activates another account

    +

    Args

    +
    +
    destination_address : str
    +
    address of the destination
    +
    starting_balance : str, optional
    +
    the balance that the destination address will start with. Must be a positive integer expressed as a string. Defaults to "12.50".
    +
    +
    + +Expand source code + +
    def activate_account(self, destination_address, starting_balance="3.6"):
    +    """Activates another account
    +
    +    Args:
    +        destination_address (str): address of the destination
    +        starting_balance (str, optional): the balance that the destination address will start with. Must be a positive integer expressed as a string. Defaults to "12.50".
    +    """
    +    server = self._get_horizon_server()
    +    source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +    source_account = self.load_account()
    +
    +    base_fee = server.fetch_base_fee()
    +    transaction = (
    +        stellar_sdk.TransactionBuilder(
    +            source_account=source_account,
    +            network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +            base_fee=base_fee,
    +        )
    +        .append_create_account_op(destination=destination_address, starting_balance=starting_balance)
    +        .build()
    +    )
    +    transaction.sign(source_keypair)
    +    try:
    +        response = server.submit_transaction(transaction)
    +        j.logger.info("Transaction hash: {}".format(response["hash"]))
    +    except stellar_sdk.exceptions.BadRequestError as e:
    +        j.logger.debug(e)
    +
    +
    +
    +def activate_through_activation_wallet(self, wallet_name='activation_wallet') +
    +
    +

    Activate your wallet through activation wallet.

    +
    + +Expand source code + +
    def activate_through_activation_wallet(self, wallet_name="activation_wallet"):
    +    """Activate your wallet through activation wallet."""
    +    if wallet_name in j.clients.stellar.list_all() and self.instance_name != wallet_name:
    +        j.logger.info(f"trying to fund the wallet ourselves with the activation wallet")
    +        j.logger.info(f"activation wallet {self.instance_name}")
    +        for _ in range(5):
    +            try:
    +                j.clients.stellar.activation_wallet.activate_account(self.address, "2.6")
    +                self.add_known_trustline("TFT")
    +                j.logger.info(f"activated wallet {self.instance_name}")
    +                return
    +            except Exception as e:
    +                j.logger.error(f"failed to activate wallet {self.instance_name} using activation_wallet")
    +    else:
    +        raise RuntimeError(f"could not find the activation wallet: {wallet_name}")
    +
    +
    +
    +def activate_through_friendbot(self) +
    +
    +

    Activates and funds a testnet account using friendbot

    +
    + +Expand source code + +
    def activate_through_friendbot(self):
    +    """Activates and funds a testnet account using friendbot"""
    +    if self.network.value != "TEST":
    +        raise Exception("Account activation through friendbot is only available on testnet")
    +
    +    resp = j.tools.http.get("https://friendbot.stellar.org", params={"addr": self.address})
    +    resp.raise_for_status()
    +    j.logger.info(f"account with address {self.address} activated and funded through friendbot")
    +
    +
    +
    +def activate_through_threefold_service(self) +
    +
    +

    Activate your wallet through threefold services

    +
    + +Expand source code + +
    def activate_through_threefold_service(self):
    +    """
    +    Activate your wallet through threefold services
    +    """
    +    for _ in range(5):
    +        j.logger.info(f"trying to activate : {self.instance_name}")
    +        try:
    +            resp = self._activation_account()
    +            loaded_json = j.data.serializers.json.loads(resp)
    +            xdr = loaded_json["activation_transaction"]
    +            self.sign(xdr, submit=True)
    +            j.logger.info(f"{self.instance_name} is activated using the activation service.")
    +            return
    +        except Exception as e:
    +            j.logger.error(f"failed to activate using the activation service {e}")
    +            ## Try activating with `activation_wallet` j.clients.stellar.activation_wallet if exists
    +            ## this activator should be imported on the system.
    +    else:
    +        raise RuntimeError(
    +            "could not activate wallet. tried activation service and there's no activation_wallet configured on the system"
    +        )
    +
    +
    +
    +def add_known_trustline(self, asset_code) +
    +
    +

    Will add a trustline known by threefold for chosen asset_code

    +

    Args

    +
    +
    asset_code : str
    +
    code of the asset. For example: 'BTC', 'TFT', …
    +
    +
    + +Expand source code + +
    def add_known_trustline(self, asset_code):
    +    """Will add a trustline known by threefold for chosen asset_code
    +
    +    Args:
    +        asset_code (str): code of the asset. For example: 'BTC', 'TFT', ...
    +    """
    +    j.logger.info(f"adding trustline {asset_code} to account {self.address}")
    +    balances = self.get_balance()
    +    for b in balances.balances:
    +        if b.asset_code == asset_code:
    +            j.logger.info(f"trustline {asset_code} is already added.")
    +            return
    +    issuer = _NETWORK_KNOWN_TRUSTS.get(self.network.value, {}).get(asset_code)
    +    if not issuer:
    +        raise j.exceptions.NotFound(f"There is no known issuer for {asset_code} on network {self.network}")
    +    self._change_trustline(asset_code, issuer)
    +
    +
    +
    +def add_trustline(self, asset_code, issuer, secret=None) +
    +
    +

    Create a trustline to an asset

    +

    Args

    +
    +
    asset_code : str
    +
    code of the asset. For example: 'BTC', 'TFT', …
    +
    issuer : str
    +
    address of the asset issuer
    +
    secret : str, optional
    +
    Secret to use will use instance property if empty. Defaults to None.
    +
    +
    + +Expand source code + +
    def add_trustline(self, asset_code, issuer, secret=None):
    +    """Create a trustline to an asset
    +
    +    Args:
    +        asset_code (str): code of the asset. For example: 'BTC', 'TFT', ...
    +        issuer (str): address of the asset issuer
    +        secret (str, optional): Secret to use will use instance property if empty. Defaults to None.
    +    """
    +    self._change_trustline(asset_code, issuer, secret=secret)
    +    j.logger.info(f"Added trustline {asset_code}:{issuer} to account {self.address}")
    +
    +
    +
    +def cancel_sell_order(self, offer_id, selling_asset: str, buying_asset: str, price: Union[str, decimal.Decimal]) +
    +
    +

    Deletes a selling order for amount amount of selling_asset for buying_asset with the price of price

    +

    Args

    +
    +
    selling_asset : str
    +
    Selling Asset
    +
    buying_asset : str
    +
    Buying Asset
    +
    offer_id : int
    +
    pass the current offer id and set the amount to 0 to cancel this offer
    +
    price : str
    +
    order price
    +
    +
    + +Expand source code + +
    def cancel_sell_order(self, offer_id, selling_asset: str, buying_asset: str, price: Union[str, decimal.Decimal]):
    +    """Deletes a selling order for amount `amount` of `selling_asset` for `buying_asset` with the price of `price`
    +
    +    Args:
    +        selling_asset (str): Selling Asset
    +        buying_asset (str): Buying Asset
    +        offer_id (int): pass the current offer id and set the amount to 0 to cancel this offer
    +        price (str): order price
    +    """
    +    return self._manage_sell_order(
    +        selling_asset=selling_asset, buying_asset=buying_asset, amount="0", price=price, offer_id=offer_id
    +    )
    +
    +
    +
    +def check_is_payment_transaction(self, transaction_hash) +
    +
    +

    Some transactions doesn't have an amount like activating the wallet +This helper method to help in iterating in transactions

    +

    Args

    +
    +
    transaction_hash : String
    +
    Transaction hash
    +
    +

    Returns

    +
    +
    Bool
    +
    True if transaction has amount - False if not
    +
    +
    + +Expand source code + +
    def check_is_payment_transaction(self, transaction_hash):
    +    """Some transactions doesn't have an amount like activating the wallet
    +    This helper method to help in iterating in transactions
    +
    +    Args:
    +        transaction_hash (String): Transaction hash
    +
    +    Returns:
    +        Bool: True if transaction has amount - False if not
    +    """
    +
    +    server = self._get_horizon_server()
    +    endpoint = server.operations().for_transaction(transaction_hash)
    +    response = endpoint.call()
    +    results = response["_embedded"]["records"][0]
    +    return results["type"] == "payment"
    +
    +
    +
    +def claim_locked_funds(self) +
    +
    +
    +
    + +Expand source code + +
    def claim_locked_funds(self):
    +    balances = self.get_balance()
    +    for locked_account in balances.escrow_accounts:
    +        if locked_account.can_be_unlocked():
    +            self._unlock_account(locked_account)
    +
    +
    +
    +def delete_trustline(self, asset_code, issuer, secret=None) +
    +
    +

    Deletes a trustline

    +

    Args

    +
    +
    asset_code : str
    +
    code of the asset. For example: 'BTC', 'XRP', …
    +
    issuer : str
    +
    address of the asset issuer
    +
    secret : str, optional
    +
    Secret to use will use instance property if empty. Defaults to None.
    +
    +
    + +Expand source code + +
    def delete_trustline(self, asset_code, issuer, secret=None):
    +    """Deletes a trustline
    +
    +    Args:
    +        asset_code (str): code of the asset. For example: 'BTC', 'XRP', ...
    +        issuer (str): address of the asset issuer
    +        secret (str, optional): Secret to use will use instance property if empty. Defaults to None.
    +    """
    +    self._change_trustline(asset_code, issuer, limit="0", secret=secret)
    +    j.logger.info(f"Removed trustline {asset_code}:{issuer} from account {self.address}")
    +
    +
    +
    +def get_balance(self, address=None) +
    +
    +

    Gets the balances for a stellar address

    +
    + +Expand source code + +
    def get_balance(self, address=None):
    +    """Gets the balances for a stellar address"""
    +    if address is None:
    +        address = self.address
    +    all_balances = self._get_free_balances(address)
    +    for account in self._find_escrow_accounts(address):
    +        all_balances.add_escrow_account(account)
    +    return all_balances
    +
    +
    +
    +def get_balance_by_asset(self, asset='TFT') ‑> float +
    +
    +
    +
    + +Expand source code + +
    def get_balance_by_asset(self, asset="TFT") -> float:
    +    balances = self.get_balance()
    +    for balance in balances.balances:
    +        if balance.asset_code == asset:
    +            return float(balance.balance)
    +    return 0
    +
    +
    +
    +def get_created_offers(self, wallet_address: str = None) +
    +
    +

    Returns a list of the currently created offers

    +

    Args

    +
    +
    wallet_address : Str, optional
    +
    wallet address you want to get offers to. Defaults to self.address.
    +
    +

    Returns

    +

    list

    +
    + +Expand source code + +
    def get_created_offers(self, wallet_address: str = None):
    +    """Returns a list of the currently created offers
    +
    +    Args:
    +        wallet_address (Str, optional): wallet address you want to get offers to. Defaults to self.address.
    +
    +    Returns:
    +        list
    +    """
    +    wallet_address = wallet_address or self.address
    +    server = self._get_horizon_server()
    +    endpoint = server.offers()
    +    endpoint.for_account(wallet_address)
    +    response = endpoint.call()
    +    offers = response["_embedded"]["records"]
    +    return offers
    +
    +
    +
    +def get_data_entries(self, address: str = None) +
    +
    +
    +
    + +Expand source code + +
    def get_data_entries(self, address: str = None):
    +    address = address or self.address
    +    horizon_server = self._get_horizon_server()
    +    response = horizon_server.accounts().account_id(address).call()
    +    data = {}
    +    for data_name, data_value in response["data"].items():
    +        data[data_name] = base64.b64decode(data_value).decode("utf-8")
    +    return data
    +
    +
    +
    +def get_sender_wallet_address(self, transaction_hash) +
    +
    +

    Get the sender's wallet address from a transaction hash

    +

    Args

    +
    +
    transaction_hash : String
    +
    Transaction hash
    +
    +

    Returns

    +
    +
    String
    +
    Wallet Hash
    +
    +
    + +Expand source code + +
    def get_sender_wallet_address(self, transaction_hash):
    +    """Get the sender's wallet address from a transaction hash
    +
    +    Args:
    +        transaction_hash (String): Transaction hash
    +
    +    Returns:
    +        String : Wallet Hash
    +
    +    """
    +    server = self._get_horizon_server()
    +    endpoint = server.operations().for_transaction(transaction_hash)
    +    response = endpoint.call()
    +    # not possible for a transaction to have more than a source, so will take first one
    +    wallet_address = response["_embedded"]["records"][0]["source_account"]
    +    return wallet_address
    +
    +
    +
    +def get_signing_requirements(self, address: str = None) +
    +
    +
    +
    + +Expand source code + +
    def get_signing_requirements(self, address: str = None):
    +    address = address or self.address
    +    response = self._get_horizon_server().accounts().account_id(address).call()
    +    signing_requirements = {}
    +    signing_requirements["thresholds"] = response["thresholds"]
    +    signing_requirements["signers"] = response["signers"]
    +    return signing_requirements
    +
    +
    +
    +def get_transaction_effects(self, transaction_hash, address=None) +
    +
    +

    Get the effects on an adddressfor a specific transaction

    +

    Args

    +
    +
    transaction_hash : str
    +
    hash of the transaction
    +
    address : str, optional
    +
    address of the effects.If None, the address of this wallet is taken. Defaults to None.
    +
    +

    Returns

    +
    +
    list
    +
    list of Effect objects
    +
    +
    + +Expand source code + +
    def get_transaction_effects(self, transaction_hash, address=None):
    +    """Get the effects on an adddressfor a specific transaction
    +
    +    Args:
    +        transaction_hash (str): hash of the transaction
    +        address (str, optional): address of the effects.If None, the address of this wallet is taken. Defaults to None.
    +
    +    Returns:
    +        list: list of Effect objects
    +    """
    +    address = address or self.address
    +    effects = []
    +    endpoint = self._get_horizon_server().effects()
    +    endpoint.for_transaction(transaction_hash)
    +    old_cursor = "old"
    +    new_cursor = ""
    +    while old_cursor != new_cursor:
    +        old_cursor = new_cursor
    +        endpoint.cursor(new_cursor)
    +        response = endpoint.call()
    +        next_link = response["_links"]["next"]["href"]
    +        next_link_query = parse.urlsplit(next_link).query
    +        new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +        response_effects = response["_embedded"]["records"]
    +        for response_effect in response_effects:
    +            if "account" in response_effect and response_effect["account"] == address:
    +                effects.append(Effect.from_horizon_response(response_effect))
    +    return effects
    +
    +
    +
    +def list_payments(self, address: str = None, asset: str = None, cursor: str = None) +
    +
    +

    Get the transactions for an adddress +:param address: address of the effects.In None, the address of this wallet is taken +:param asset: stellar asset in the code:issuer form( except for XLM, which does not need an issuer) +:param cursor:pass a cursor to continue after the last call or an empty str to start receivibg a cursor +if a cursor is passed, a tuple of the payments and the cursor is returned

    +
    + +Expand source code + +
    def list_payments(self, address: str = None, asset: str = None, cursor: str = None):
    +    """Get the transactions for an adddress
    +    :param address: address of the effects.In None, the address of this wallet is taken
    +    :param asset: stellar asset in the code:issuer form( except for XLM, which does not need an issuer)
    +    :param cursor:pass a cursor to continue after the last call or an empty str to start receivibg a cursor
    +     if a cursor is passed, a tuple of the payments and the cursor is returned
    +    """
    +    if address is None:
    +        address = self.address
    +    tx_endpoint = self._get_horizon_server().payments()
    +    tx_endpoint.for_account(address)
    +    tx_endpoint.limit(50)
    +    payments = []
    +    old_cursor = "old"
    +    new_cursor = ""
    +    if cursor is not None:
    +        new_cursor = cursor
    +    while old_cursor != new_cursor:
    +        old_cursor = new_cursor
    +        tx_endpoint.cursor(new_cursor)
    +        response = tx_endpoint.call()
    +        next_link = response["_links"]["next"]["href"]
    +        next_link_query = parse.urlsplit(next_link).query
    +        new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +        response_payments = response["_embedded"]["records"]
    +        for response_payment in response_payments:
    +            ps = PaymentSummary.from_horizon_response(response_payment, address)
    +            if asset:
    +                split_asset = asset.split(":")
    +                assetcode = split_asset[0]
    +                assetissuer = None
    +                if len(split_asset) > 1:
    +                    assetissuer = split_asset[1]
    +                if ps.balance and ps.balance.asset_code == assetcode:
    +                    if assetissuer and assetissuer == ps.balance.asset_issuer:
    +                        payments.append(ps)
    +            else:
    +                payments.append(ps)
    +    if cursor is not None:
    +        return {"payments": payments, "cursor": new_cursor}
    +
    +    return payments
    +
    +
    +
    +def list_transactions(self, address: str = None, cursor: str = None) +
    +
    +

    Get the transactions for an adddres +:param address (str, optional): address of the effects.If None, the address of this wallet is taken. Defaults to None. +:param cursor:pass a cursor to continue after the last call or an empty str to start receivibg a cursor +if a cursor is passed, a tuple of the payments and the cursor is returned

    +

    Returns

    +
    +
    list
    +
    list of TransactionSummary objects
    +
    dictionary
    +
    {"transactions":list of TransactionSummary objects, "cursor":cursor}
    +
    +
    + +Expand source code + +
    def list_transactions(self, address: str = None, cursor: str = None):
    +    """Get the transactions for an adddres
    +    :param address (str, optional): address of the effects.If None, the address of this wallet is taken. Defaults to None.
    +    :param cursor:pass a cursor to continue after the last call or an empty str to start receivibg a cursor
    +     if a cursor is passed, a tuple of the payments and the cursor is returned
    +
    +    Returns:
    +        list: list of TransactionSummary objects
    +        dictionary: {"transactions":list of TransactionSummary objects, "cursor":cursor}
    +    """
    +    address = address or self.address
    +    tx_endpoint = self._get_horizon_server().transactions()
    +    tx_endpoint.for_account(address)
    +    tx_endpoint.include_failed(True)
    +    transactions = []
    +    old_cursor = "old"
    +    new_cursor = ""
    +    if cursor is not None:
    +        new_cursor = cursor
    +    while old_cursor != new_cursor:
    +        old_cursor = new_cursor
    +        tx_endpoint.cursor(new_cursor)
    +        response = tx_endpoint.call()
    +        next_link = response["_links"]["next"]["href"]
    +        next_link_query = parse.urlsplit(next_link).query
    +        new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +        response_transactions = response["_embedded"]["records"]
    +        for response_transaction in response_transactions:
    +            if response_transaction["successful"]:
    +                transactions.append(TransactionSummary.from_horizon_response(response_transaction))
    +
    +    if cursor is not None:
    +        return {"transactions": transactions, "cursor": new_cursor}
    +    return transactions
    +
    +
    +
    +def load_account(self) +
    +
    +
    +
    + +Expand source code + +
    def load_account(self):
    +    horizonServer = self._get_horizon_server()
    +    saccount = horizonServer.load_account(self.address)
    +    account = Account(saccount.account.account_id, saccount.sequence, self)
    +    return account
    +
    +
    +
    +def merge_into_account(self, destination_address: str) +
    +
    +

    Merges XLMs into destination address

    +

    Args

    +
    +
    destination_address : str
    +
    address to send XLMs to
    +
    +
    + +Expand source code + +
    def merge_into_account(self, destination_address: str):
    +    """Merges XLMs into destination address
    +
    +    Args:
    +        destination_address (str): address to send XLMs to
    +    """
    +    server = self._get_horizon_server()
    +    source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +    source_account = self.load_account()
    +
    +    base_fee = server.fetch_base_fee()
    +
    +    transaction_builder = stellar_sdk.TransactionBuilder(
    +        source_account=source_account,
    +        network_passphrase=_NETWORK_PASSPHRASES[self.network.value],
    +        base_fee=base_fee,
    +    )
    +
    +    balances = self.get_balance()
    +    for balance in balances.balances:
    +        if balance.is_native():
    +            continue
    +        # Step 1: Transfer custom assets
    +        if decimal.Decimal(balance.balance) > decimal.Decimal(0):
    +            transaction_builder.append_payment_op(
    +                destination=destination_address,
    +                amount=balance.balance,
    +                asset_code=balance.asset_code,
    +                asset_issuer=balance.asset_issuer,
    +            )
    +        # Step 2: Delete trustlines
    +        transaction_builder.append_change_trust_op(
    +            asset_issuer=balance.asset_issuer, asset_code=balance.asset_code, limit="0"
    +        )
    +
    +    transaction_builder.append_account_merge_op(destination=destination_address)
    +    transaction = transaction_builder.build()
    +
    +    transaction.sign(source_keypair)
    +    try:
    +        response = server.submit_transaction(transaction)
    +        j.logger.info("Transaction hash: {}".format(response["hash"]))
    +    except stellar_sdk.exceptions.BadRequestError as e:
    +        j.logger.debug(e)
    +
    +
    +
    +def modify_signing_requirements(self, public_keys_signers, signature_count, low_treshold=0, high_treshold=2, master_weight=2) +
    +
    +

    modify_signing_requirements sets to amount of signatures required for the creation of multisig account. It also adds +the public keys of the signer to this account

    +

    Args

    +
    +
    public_keys_signers : list
    +
    list of public keys of signers
    +
    signature_count : int
    +
    amount of signatures requires to transfer funds
    +
    low_treshold : int, optional
    +
    amount of signatures required for low security operations (transaction processing, allow trust, bump sequence). Defaults to 1.
    +
    high_treshold : int, optional
    +
    amount of signatures required for high security operations (set options, account merge). Defaults to 2.
    +
    master_weight : int, optional
    +
    A number from 0-255 (inclusive) representing the weight of the master key. If the weight of the master key is updated to 0, it is effectively disabled. Defaults to 2.
    +
    +
    + +Expand source code + +
    def modify_signing_requirements(
    +    self, public_keys_signers, signature_count, low_treshold=0, high_treshold=2, master_weight=2
    +):
    +    """modify_signing_requirements sets to amount of signatures required for the creation of multisig account. It also adds
    +       the public keys of the signer to this account
    +
    +    Args:
    +        public_keys_signers (list): list of public keys of signers
    +        signature_count (int): amount of signatures requires to transfer funds
    +        low_treshold (int, optional): amount of signatures required for low security operations (transaction processing, allow trust, bump sequence). Defaults to 1.
    +        high_treshold (int, optional): amount of signatures required for high security operations (set options, account merge). Defaults to 2.
    +        master_weight (int, optional): A number from 0-255 (inclusive) representing the weight of the master key. If the weight of the master key is updated to 0, it is effectively disabled. Defaults to 2.
    +    """
    +    server = self._get_horizon_server()
    +    account = self.load_account()
    +    source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +    transaction_builder = stellar_sdk.TransactionBuilder(
    +        account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value]
    +    )
    +    # set the signing options
    +    transaction_builder.append_set_options_op(
    +        low_threshold=low_treshold,
    +        med_threshold=signature_count,
    +        high_threshold=high_treshold,
    +        master_weight=master_weight,
    +    )
    +
    +    # For every public key given, add it as a signer to this account
    +    for public_key_signer in public_keys_signers:
    +        transaction_builder.append_ed25519_public_key_signer(public_key_signer, 1)
    +
    +    transaction_builder.set_timeout(30)
    +    tx = transaction_builder.build()
    +    tx.sign(source_keypair)
    +
    +    try:
    +        response = server.submit_transaction(tx)
    +        j.logger.info(response)
    +        j.logger.info(f"Set the signers of {self.address} to require {signature_count} signers")
    +    except stellar_sdk.exceptions.BadRequestError:
    +        j.logger.info("Transaction need additional signatures in order to send")
    +        return tx.to_xdr()
    +
    +
    +
    +def place_sell_order(self, selling_asset: str, buying_asset: str, amount: Union[str, decimal.Decimal], price: Union[str, decimal.Decimal], timeout=30, offer_id=0) +
    +
    +

    Places/Deletes a selling order for amount amount of selling_asset for buying_asset with the price of price

    +

    Args

    +
    +
    selling_asset : str
    +
    Selling Asset
    +
    buying_asset str): Buying Asset
    +
    amount : Union[str, decimal.Decimal]
    +
    Amount to sell.
    +
    price : Union[str, decimal.Decimal]
    +
    Price for selling.
    +
    timeout : int, optional
    +
    Timeout for submitting the transaction. Defaults to 30.
    +
    offer_id
    +
    pass the current offer id and set the amount to 0 to cancel this offer or another amount to update the offer
    +
    +

    Raises

    +
    +
    ValueError
    +
    In case of invalid issuer.
    +
    RuntimeError
    +
    Error happened during submission of the transaction.
    +
    +

    Returns

    +

    (dict): response as the result of sumbit the transaction

    +
    + +Expand source code + +
    def _manage_sell_order(
    +    self,
    +    selling_asset: str,
    +    buying_asset: str,
    +    amount: Union[str, decimal.Decimal],
    +    price: Union[str, decimal.Decimal],
    +    timeout=30,
    +    offer_id=0,
    +):
    +    """Places/Deletes a selling order for amount `amount` of `selling_asset` for `buying_asset` with the price of `price`
    +
    +    Args:
    +        selling_asset (str): Selling Asset
    +        buying_asset str): Buying Asset
    +        amount (Union[str, decimal.Decimal]): Amount to sell.
    +        price (Union[str, decimal.Decimal]): Price for selling.
    +        timeout (int, optional): Timeout for submitting the transaction. Defaults to 30.
    +        offer_id: pass the current offer id and set the amount to 0 to cancel this offer or another amount to update the offer
    +
    +    Raises:
    +        ValueError: In case of invalid issuer.
    +        RuntimeError: Error happened during submission of the transaction.
    +
    +    Returns:
    +        (dict): response as the result of sumbit the transaction
    +    """
    +    stellar_selling_asset = self._get_asset(selling_asset)
    +    stellar_buying_asset = self._get_asset(buying_asset)
    +    server = self._get_horizon_server()
    +    tb = TransactionBuilder(self.load_account(), network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +    try:
    +        tx = (
    +            tb.append_manage_sell_offer_op(
    +                selling_code=stellar_selling_asset.code,
    +                selling_issuer=stellar_selling_asset.issuer,
    +                buying_code=stellar_buying_asset.code,
    +                buying_issuer=stellar_buying_asset.issuer,
    +                amount=amount,
    +                price=price,
    +                offer_id=offer_id,
    +            )
    +            .set_timeout(timeout)
    +            .build()
    +        )
    +    except stellar_sdk.exceptions.AssetIssuerInvalidError as e:
    +        raise ValueError("invalid issuer") from e
    +    except Exception as e:
    +        raise RuntimeError(
    +            f"error while creating order for selling: {selling_asset}, buying: {buying_asset}, amount: {amount} price: {price}"
    +        ) from e
    +    else:
    +        tx.sign(self.secret)
    +        try:
    +            resp = server.submit_transaction(tx)
    +        except Exception as e:
    +            raise RuntimeError(
    +                f"couldn't submit sell offer, probably wallet is unfunded. Please check the error stacktrace for more information."
    +            ) from e
    +        return resp
    +
    +
    +
    +def remove_signer(self, public_key_signer) +
    +
    +

    remove_signer removes a public key as a signer from the source account

    +

    Args

    +
    +
    public_key_signer : str
    +
    public key of an account
    +
    +
    + +Expand source code + +
    def remove_signer(self, public_key_signer):
    +    """remove_signer removes a public key as a signer from the source account
    +
    +    Args:
    +        public_key_signer (str): public key of an account
    +    """
    +    server = self._get_horizon_server()
    +    account = self.load_account()
    +    tx = (
    +        stellar_sdk.TransactionBuilder(account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value])
    +        .append_ed25519_public_key_signer(public_key_signer, 0)
    +        .build()
    +    )
    +
    +    source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +
    +    tx.sign(source_keypair)
    +    try:
    +        response = server.submit_transaction(tx)
    +        j.logger.info(response)
    +        j.logger.info("Multisig tx signed and sent")
    +    except stellar_sdk.exceptions.BadRequestError:
    +        j.logger.info("Transaction need additional signatures in order to send")
    +        return tx.to_xdr()
    +
    +
    +
    +def return_xlms_to_activation(self) +
    +
    +
    +
    + +Expand source code + +
    def return_xlms_to_activation(self):
    +    xlm_balance = 0
    +    for balance in self.get_balances():
    +        if balance.asset_code == "XLM":
    +            xlm_balance = balance.balance
    +    trustlines = len(self.get_balances()) - 1
    +    minimum_balance = 1 + 0.5 * trustlines
    +    amount = xlm_balance - minimum_balance - XLM_TRANSACTION_FEES
    +    self.transfer(ACTIVATION_ADDRESS, amount)
    +
    +
    +
    +def secret_updated(self, value) +
    +
    +
    +
    + +Expand source code + +
    def secret_updated(self, value):
    +    self.address = stellar_sdk.Keypair.from_secret(value).public_key
    +
    +
    +
    +def set_data_entry(self, name: str, value: str, address: str = None) +
    +
    +

    Sets, modifies or deletes a data entry (name/value pair) for an account

    +

    To delete a data entry, set the value to an empty string.

    +
    + +Expand source code + +
    def set_data_entry(self, name: str, value: str, address: str = None):
    +    """Sets, modifies or deletes a data entry (name/value pair) for an account
    +
    +    To delete a data entry, set the value to an empty string.
    +
    +    """
    +
    +    address = address or self.address
    +    signing_key = stellar_sdk.Keypair.from_secret(self.secret)
    +    horizon_server = self._get_horizon_server()
    +    if address == self.address:
    +        account = self.load_account()
    +    else:
    +        account = horizon_server.load_account(address)
    +    base_fee = horizon_server.fetch_base_fee()
    +    transaction = (
    +        stellar_sdk.TransactionBuilder(
    +            source_account=account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee
    +        )
    +        .append_manage_data_op(name, value)
    +        .set_timeout(30)
    +        .build()
    +    )
    +
    +    transaction.sign(signing_key)
    +
    +    try:
    +        response = horizon_server.submit_transaction(transaction)
    +        j.logger.info("Transaction hash: {}".format(response["hash"]))
    +    except stellar_sdk.exceptions.BadRequestError as e:
    +        j.logger.debug(e)
    +        raise e
    +
    +
    +
    +def set_unlock_transaction(self, unlock_transaction) +
    +
    +

    Adds a xdr encoded unlocktransaction +:param unlock_transaction: xdr encoded unlocktransactionaddress of the destination. +:type destination_address: str

    +
    + +Expand source code + +
    def set_unlock_transaction(self, unlock_transaction):
    +    """
    +    Adds a xdr encoded unlocktransaction
    +    :param unlock_transaction: xdr encoded unlocktransactionaddress of the destination.
    +    :type destination_address: str
    +    """
    +    txe = stellar_sdk.TransactionEnvelope.from_xdr(unlock_transaction, _NETWORK_PASSPHRASES[self.network.value])
    +    tx_hash = txe.hash()
    +    unlock_hash = stellar_sdk.strkey.StrKey.encode_pre_auth_tx(tx_hash)
    +
    +    self._create_unlockhash_transaction(unlock_hash=unlock_hash, transaction_xdr=txe.to_xdr())
    +
    +
    +
    +def sign(self, tx_xdr: str, submit: bool = True) +
    +
    +

    sign signs a transaction xdr and optionally submits it to the network

    +

    Args

    +
    +
    tx_xdr : str
    +
    transaction to sign in xdr format
    +
    submit : bool,optional
    +
    submit the transaction tro the Stellar network
    +
    +
    + +Expand source code + +
    def sign(self, tx_xdr: str, submit: bool = True):
    +    """sign signs a transaction xdr and optionally submits it to the network
    +
    +    Args:
    +        tx_xdr (str): transaction to sign in xdr format
    +        submit (bool,optional): submit the transaction tro the Stellar network
    +    """
    +
    +    source_keypair = stellar_sdk.Keypair.from_secret(self.secret)
    +    tx = stellar_sdk.TransactionEnvelope.from_xdr(tx_xdr, _NETWORK_PASSPHRASES[self.network.value])
    +    tx.sign(source_keypair)
    +    if submit:
    +        horizon_server = self._get_horizon_server()
    +        horizon_server.submit_transaction(tx)
    +    else:
    +        return tx.to_xdr()
    +
    +
    +
    +def sign_multisig_transaction(self, tx_xdr) +
    +
    +

    sign_multisig_transaction signs a transaction xdr and tries to submit it to the network

    +

    Deprecated, use sign instead

    +

    Args

    +
    +
    tx_xdr : str
    +
    transaction to sign in xdr format
    +
    +
    + +Expand source code + +
    def sign_multisig_transaction(self, tx_xdr):
    +    """sign_multisig_transaction signs a transaction xdr and tries to submit it to the network
    +
    +       Deprecated, use sign instead
    +
    +    Args:
    +        tx_xdr (str): transaction to sign in xdr format
    +    """
    +
    +    try:
    +        self.sign(tx_xdr)
    +        j.logger.info("Multisig tx signed and sent")
    +    except UnAuthorized as e:
    +        j.logger.info("Transaction needs additional signatures in order to send")
    +        return e.transaction_xdr
    +
    +
    +
    +def transfer(self, destination_address, amount, asset='XLM', locked_until=None, memo_text=None, memo_hash=None, fund_transaction=True, from_address=None, timeout=30, sequence_number: int = None, sign: bool = True, retries: int = 5) +
    +
    +

    Transfer assets to another address

    +

    Args

    +
    +
    destination_address : str
    +
    address of the destination
    +
    amount : str
    +
    can be a floating point number with 7 numbers after the decimal point expressed as a string
    +
    asset : str, optional
    +
    asset to transfer. Defaults to "XLM". if you wish to specify an asset it should be in format 'assetcode:issuer'. Where issuer is the address of the
    +
    issuer of the asset.
    +
    locked_until : float, optional
    +
    epoch timestamp indicating until when the tokens +should be locked. Defaults to None.
    +
    memo_text : Union[str, bytes], optional
    +
    memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long. Defaults to None.
    +
    memo_hash : Union[str, bytes], optional
    +
    memo hash to add to the transaction, A 32 byte hash. Defaults to None.
    +
    fund_transaction : bool, optional
    +
    use the threefoldfoundation transaction funding service. Defautls to True.
    +
    from_address : str, optional
    +
    Use a different address to send the tokens from, useful in multisig use cases. Defaults to None.
    +
    timeout (int,optional: Seconds from now on until when the transaction to be submitted to the stellar network
    +
    sequence_number : int,optional
    +
    specify a specific sequence number ( will still be increased by one) instead of loading it from the account
    +
    +

    sign (bool,optional) : Do not sign and submit the transaction

    +

    Raises

    +
    +
    Exception
    +
    If asset not in correct format
    +
    stellar_sdk.exceptions.BadRequestError
    +
    not enough funds for opertaion
    +
    stellar_sdk.exceptions.BadRequestError
    +
    bad transfer authentication
    +
    +

    Returns

    +
    +
    [type]
    +
    [description]
    +
    +
    + +Expand source code + +
    def transfer(
    +    self,
    +    destination_address,
    +    amount,
    +    asset="XLM",
    +    locked_until=None,
    +    memo_text=None,
    +    memo_hash=None,
    +    fund_transaction=True,
    +    from_address=None,
    +    timeout=30,
    +    sequence_number: int = None,
    +    sign: bool = True,
    +    retries: int = 5,
    +):
    +    """Transfer assets to another address
    +
    +    Args:
    +        destination_address (str): address of the destination
    +        amount (str): can be a floating point number with 7 numbers after the decimal point expressed as a string
    +        asset (str, optional): asset to transfer. Defaults to "XLM". if you wish to specify an asset it should be in format 'assetcode:issuer'. Where issuer is the address of the
    +        issuer of the asset.
    +        locked_until (float, optional): epoch timestamp indicating until when the tokens  should be locked. Defaults to None.
    +        memo_text (Union[str, bytes], optional): memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long. Defaults to None.
    +        memo_hash (Union[str, bytes], optional): memo hash to add to the transaction, A 32 byte hash. Defaults to None.
    +        fund_transaction (bool, optional): use the threefoldfoundation transaction funding service. Defautls to True.
    +        from_address (str, optional): Use a different address to send the tokens from, useful in multisig use cases. Defaults to None.
    +        timeout (int,optional: Seconds from now on until when the transaction to be submitted to the stellar network
    +        sequence_number (int,optional): specify a specific sequence number ( will still be increased by one) instead of loading it from the account
    +        sign (bool,optional) : Do not sign and submit the transaction
    +
    +    Raises:
    +        Exception: If asset not in correct format
    +        stellar_sdk.exceptions.BadRequestError: not enough funds for opertaion
    +        stellar_sdk.exceptions.BadRequestError: bad transfer authentication
    +
    +    Returns:
    +        [type]: [description]
    +    """
    +    if decimal.Decimal(amount) <= 0:
    +        j.logger.warning("Can not transfer empty or zero amount transaction")
    +        return
    +    nretries = 0
    +    while nretries < retries:
    +        try:
    +            return self._transfer(
    +                destination_address=destination_address,
    +                amount=amount,
    +                asset=asset,
    +                locked_until=locked_until,
    +                memo_text=memo_text,
    +                memo_hash=memo_hash,
    +                fund_transaction=fund_transaction,
    +                from_address=from_address,
    +                timeout=timeout,
    +                sequence_number=sequence_number,
    +                sign=sign,
    +            )
    +        except Exception as e:
    +            nretries += 1
    +            gevent.sleep(1)
    +            j.logger.warning(str(e))
    +
    +    raise j.exceptions.Runtime(f"Failed to make transaction for {retries} times, Please try again later")
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/stellar/transaction.html b/docs/api/jumpscale/clients/stellar/transaction.html new file mode 100644 index 000000000..09312591f --- /dev/null +++ b/docs/api/jumpscale/clients/stellar/transaction.html @@ -0,0 +1,536 @@ + + + + + + +jumpscale.clients.stellar.transaction API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.stellar.transaction

    +
    +
    +
    + +Expand source code + +
    from .balance import Balance
    +import decimal, base64, binascii
    +
    +
    +class TransactionSummary:
    +    def __init__(self, hash, memo_text=None, memo_hash=None, created_at=None):
    +        self.hash = hash
    +        self.memo_text = memo_text
    +        self.memo_hash = memo_hash
    +        self.created_at = created_at
    +
    +    @property
    +    def memo_hash_as_hex(self):
    +        if not self.memo_hash:
    +            return None
    +        return binascii.hexlify(base64.b64decode(self.memo_hash)).decode("utf-8")
    +
    +    @staticmethod
    +    def from_horizon_response(response_transaction):
    +        hash = response_transaction["hash"]
    +        created_at = response_transaction["created_at"]
    +        memo_text = None
    +        memo_hash = None
    +        if "memo" in response_transaction:
    +            if response_transaction["memo_type"] == "text":
    +                memo_text = response_transaction["memo"]
    +            if response_transaction["memo_type"] == "hash":
    +                memo_hash = response_transaction["memo"]
    +        return TransactionSummary(hash, memo_text, memo_hash, created_at)
    +
    +    def __str__(self):
    +        representation = f"{self.hash} created at {self.created_at}"
    +        if self.memo_text is not None:
    +            representation += f" with memo text '{self.memo_text}'"
    +        if self.memo_hash is not None:
    +            representation += f" with memo hash '{self.memo_hash}'"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +class Effect:
    +    def __init__(self, amount=0.0, asset_code="XLM", asset_issuer=None):
    +        self.amount = amount
    +        self.asset_code = asset_code
    +        self.asset_issuer = asset_issuer
    +
    +    @staticmethod
    +    def from_horizon_response(response_effect):
    +        amount = decimal.Decimal(response_effect["amount"])
    +        if response_effect["asset_type"] == "native":
    +            asset_code = "XLM"
    +            asset_issuer = None
    +        else:
    +            asset_code = response_effect["asset_code"]
    +            asset_issuer = response_effect["asset_issuer"]
    +        if "type" in response_effect and response_effect["type"] == "account_debited":
    +            amount = -amount
    +        return Effect(amount, asset_code, asset_issuer)
    +
    +    def __str__(self):
    +        balance = Balance(self.amount, self.asset_code, self.asset_issuer)
    +        representation = str(balance)
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +class PaymentSummary(object):
    +    def __init__(
    +        self,
    +        transaction_hash,
    +        balance: Balance,
    +        payment_type: str,
    +        created_at,
    +        from_address: str,
    +        to_address: str,
    +        my_address: str,
    +    ):
    +        self.balance = balance
    +        self.created_at = created_at
    +        self.from_address = from_address
    +        self.to_address = to_address
    +        self.payment_type = payment_type
    +        self.transaction_hash = transaction_hash
    +        self.my_address = my_address
    +
    +    @staticmethod
    +    def from_horizon_response(response_payment, my_address: str):
    +        transaction_hash = response_payment["transaction_hash"]
    +        created_at = response_payment["created_at"]
    +        payment_type = response_payment["type"]
    +
    +        if payment_type == "create_account":
    +            return PaymentSummary(
    +                transaction_hash,
    +                Balance(response_payment["starting_balance"]),
    +                payment_type,
    +                created_at,
    +                response_payment["funder"],
    +                response_payment["account"],
    +                my_address,
    +            )
    +
    +        if payment_type == "account_merge":
    +            return PaymentSummary(
    +                transaction_hash,
    +                None,
    +                payment_type,
    +                created_at,
    +                response_payment["account"],
    +                response_payment["into"],
    +                my_address,
    +            )
    +
    +        balance = Balance(response_payment["amount"])
    +        if response_payment["asset_type"] != "native":
    +            balance.asset_code = response_payment["asset_code"]
    +            balance.asset_issuer = response_payment["asset_issuer"]
    +        return PaymentSummary(
    +            transaction_hash,
    +            balance,
    +            payment_type,
    +            created_at,
    +            response_payment["from"],
    +            response_payment["to"],
    +            my_address,
    +        )
    +
    +    def __str__(self):
    +        if self.payment_type == "create_account":
    +            if self.to_address == self.my_address:
    +                representation = f"Account created with {self.balance} by {self.from_address}"
    +            else:
    +                representation = f"Created account {self.to_address} with {self.balance}"
    +        elif self.payment_type == "account_merge":
    +            representation = f"Account {self.from_address} merged"
    +        else:
    +            if self.to_address == self.my_address:
    +                representation = f"{self.balance} received from {self.from_address}"
    +            else:
    +                representation = f"{self.balance} sent to {self.to_address}"
    +
    +        representation += f" at {self.created_at} tx: {self.transaction_hash}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Effect +(amount=0.0, asset_code='XLM', asset_issuer=None) +
    +
    +
    +
    + +Expand source code + +
    class Effect:
    +    def __init__(self, amount=0.0, asset_code="XLM", asset_issuer=None):
    +        self.amount = amount
    +        self.asset_code = asset_code
    +        self.asset_issuer = asset_issuer
    +
    +    @staticmethod
    +    def from_horizon_response(response_effect):
    +        amount = decimal.Decimal(response_effect["amount"])
    +        if response_effect["asset_type"] == "native":
    +            asset_code = "XLM"
    +            asset_issuer = None
    +        else:
    +            asset_code = response_effect["asset_code"]
    +            asset_issuer = response_effect["asset_issuer"]
    +        if "type" in response_effect and response_effect["type"] == "account_debited":
    +            amount = -amount
    +        return Effect(amount, asset_code, asset_issuer)
    +
    +    def __str__(self):
    +        balance = Balance(self.amount, self.asset_code, self.asset_issuer)
    +        representation = str(balance)
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +

    Static methods

    +
    +
    +def from_horizon_response(response_effect) +
    +
    +
    +
    + +Expand source code + +
    @staticmethod
    +def from_horizon_response(response_effect):
    +    amount = decimal.Decimal(response_effect["amount"])
    +    if response_effect["asset_type"] == "native":
    +        asset_code = "XLM"
    +        asset_issuer = None
    +    else:
    +        asset_code = response_effect["asset_code"]
    +        asset_issuer = response_effect["asset_issuer"]
    +    if "type" in response_effect and response_effect["type"] == "account_debited":
    +        amount = -amount
    +    return Effect(amount, asset_code, asset_issuer)
    +
    +
    +
    +
    +
    +class PaymentSummary +(transaction_hash, balance: Balance, payment_type: str, created_at, from_address: str, to_address: str, my_address: str) +
    +
    +
    +
    + +Expand source code + +
    class PaymentSummary(object):
    +    def __init__(
    +        self,
    +        transaction_hash,
    +        balance: Balance,
    +        payment_type: str,
    +        created_at,
    +        from_address: str,
    +        to_address: str,
    +        my_address: str,
    +    ):
    +        self.balance = balance
    +        self.created_at = created_at
    +        self.from_address = from_address
    +        self.to_address = to_address
    +        self.payment_type = payment_type
    +        self.transaction_hash = transaction_hash
    +        self.my_address = my_address
    +
    +    @staticmethod
    +    def from_horizon_response(response_payment, my_address: str):
    +        transaction_hash = response_payment["transaction_hash"]
    +        created_at = response_payment["created_at"]
    +        payment_type = response_payment["type"]
    +
    +        if payment_type == "create_account":
    +            return PaymentSummary(
    +                transaction_hash,
    +                Balance(response_payment["starting_balance"]),
    +                payment_type,
    +                created_at,
    +                response_payment["funder"],
    +                response_payment["account"],
    +                my_address,
    +            )
    +
    +        if payment_type == "account_merge":
    +            return PaymentSummary(
    +                transaction_hash,
    +                None,
    +                payment_type,
    +                created_at,
    +                response_payment["account"],
    +                response_payment["into"],
    +                my_address,
    +            )
    +
    +        balance = Balance(response_payment["amount"])
    +        if response_payment["asset_type"] != "native":
    +            balance.asset_code = response_payment["asset_code"]
    +            balance.asset_issuer = response_payment["asset_issuer"]
    +        return PaymentSummary(
    +            transaction_hash,
    +            balance,
    +            payment_type,
    +            created_at,
    +            response_payment["from"],
    +            response_payment["to"],
    +            my_address,
    +        )
    +
    +    def __str__(self):
    +        if self.payment_type == "create_account":
    +            if self.to_address == self.my_address:
    +                representation = f"Account created with {self.balance} by {self.from_address}"
    +            else:
    +                representation = f"Created account {self.to_address} with {self.balance}"
    +        elif self.payment_type == "account_merge":
    +            representation = f"Account {self.from_address} merged"
    +        else:
    +            if self.to_address == self.my_address:
    +                representation = f"{self.balance} received from {self.from_address}"
    +            else:
    +                representation = f"{self.balance} sent to {self.to_address}"
    +
    +        representation += f" at {self.created_at} tx: {self.transaction_hash}"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +

    Static methods

    +
    +
    +def from_horizon_response(response_payment, my_address: str) +
    +
    +
    +
    + +Expand source code + +
    @staticmethod
    +def from_horizon_response(response_payment, my_address: str):
    +    transaction_hash = response_payment["transaction_hash"]
    +    created_at = response_payment["created_at"]
    +    payment_type = response_payment["type"]
    +
    +    if payment_type == "create_account":
    +        return PaymentSummary(
    +            transaction_hash,
    +            Balance(response_payment["starting_balance"]),
    +            payment_type,
    +            created_at,
    +            response_payment["funder"],
    +            response_payment["account"],
    +            my_address,
    +        )
    +
    +    if payment_type == "account_merge":
    +        return PaymentSummary(
    +            transaction_hash,
    +            None,
    +            payment_type,
    +            created_at,
    +            response_payment["account"],
    +            response_payment["into"],
    +            my_address,
    +        )
    +
    +    balance = Balance(response_payment["amount"])
    +    if response_payment["asset_type"] != "native":
    +        balance.asset_code = response_payment["asset_code"]
    +        balance.asset_issuer = response_payment["asset_issuer"]
    +    return PaymentSummary(
    +        transaction_hash,
    +        balance,
    +        payment_type,
    +        created_at,
    +        response_payment["from"],
    +        response_payment["to"],
    +        my_address,
    +    )
    +
    +
    +
    +
    +
    +class TransactionSummary +(hash, memo_text=None, memo_hash=None, created_at=None) +
    +
    +
    +
    + +Expand source code + +
    class TransactionSummary:
    +    def __init__(self, hash, memo_text=None, memo_hash=None, created_at=None):
    +        self.hash = hash
    +        self.memo_text = memo_text
    +        self.memo_hash = memo_hash
    +        self.created_at = created_at
    +
    +    @property
    +    def memo_hash_as_hex(self):
    +        if not self.memo_hash:
    +            return None
    +        return binascii.hexlify(base64.b64decode(self.memo_hash)).decode("utf-8")
    +
    +    @staticmethod
    +    def from_horizon_response(response_transaction):
    +        hash = response_transaction["hash"]
    +        created_at = response_transaction["created_at"]
    +        memo_text = None
    +        memo_hash = None
    +        if "memo" in response_transaction:
    +            if response_transaction["memo_type"] == "text":
    +                memo_text = response_transaction["memo"]
    +            if response_transaction["memo_type"] == "hash":
    +                memo_hash = response_transaction["memo"]
    +        return TransactionSummary(hash, memo_text, memo_hash, created_at)
    +
    +    def __str__(self):
    +        representation = f"{self.hash} created at {self.created_at}"
    +        if self.memo_text is not None:
    +            representation += f" with memo text '{self.memo_text}'"
    +        if self.memo_hash is not None:
    +            representation += f" with memo hash '{self.memo_hash}'"
    +        return representation
    +
    +    def __repr__(self):
    +        return str(self)
    +
    +

    Static methods

    +
    +
    +def from_horizon_response(response_transaction) +
    +
    +
    +
    + +Expand source code + +
    @staticmethod
    +def from_horizon_response(response_transaction):
    +    hash = response_transaction["hash"]
    +    created_at = response_transaction["created_at"]
    +    memo_text = None
    +    memo_hash = None
    +    if "memo" in response_transaction:
    +        if response_transaction["memo_type"] == "text":
    +            memo_text = response_transaction["memo"]
    +        if response_transaction["memo_type"] == "hash":
    +            memo_hash = response_transaction["memo"]
    +    return TransactionSummary(hash, memo_text, memo_hash, created_at)
    +
    +
    +
    +

    Instance variables

    +
    +
    var memo_hash_as_hex
    +
    +
    +
    + +Expand source code + +
    @property
    +def memo_hash_as_hex(self):
    +    if not self.memo_hash:
    +        return None
    +    return binascii.hexlify(base64.b64decode(self.memo_hash)).decode("utf-8")
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/stellar/vesting.html b/docs/api/jumpscale/clients/stellar/vesting.html new file mode 100644 index 000000000..32a02b1b2 --- /dev/null +++ b/docs/api/jumpscale/clients/stellar/vesting.html @@ -0,0 +1,230 @@ + + + + + + +jumpscale.clients.stellar.vesting API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.stellar.vesting

    +
    +
    +
    + +Expand source code + +
    import stellar_sdk
    +from jumpscale.loader import j
    +
    +
    +_TFT_ISSUERS = {
    +    "TEST": "GA47YZA3PKFUZMPLQ3B5F2E3CJIB57TGGU7SPCQT2WAEYKN766PWIMB3",
    +    "STD": "GBOVQKJYHXRR3DX6NOX2RRYFRCUMSADGDESTDNBDS6CDVLGVESRTAC47",
    +}
    +
    +_VESTING_ACTIVATORS = {
    +    "TEST": "GAXSJCVW7FBQCW3COY5B67S5QB6PLKTFL6ATLYXLZGCACO3KLYA6FVI7",
    +    "STD": "GB2P3V4GZNYTI3E5IOQHAF2E46GKMW26RHVTD4TZEZAELKLJHUYPH4FR",
    +}
    +
    +
    +_COSIGNERS = {
    +    "TEST": [
    +        "GBOZV7I3NJGXIFBMGHTETCY7ZL36QMER25IJGRFEEX3KM36YRUMHEZAP",
    +        "GCU5OGRCFJ3NWQWUF3A4MAGNYABOPJMCHMXYQ7TC4RQ6IYBIZBEOGN6H",
    +        "GAK2Q3P3YOAPK6MCM4P3ASDJYSHQ376LIO64CHBXNKIRFZECH2JY2LYX",
    +        "GALOCGHPJ2KQ67VIZRSLOAPJIBCUL54WG6ZHFDB6AKPIMEQXHU374UCJ",
    +        "GC3IFIJ7KEOZ5CUZS57HMLJLXISS7N45INIGXO2JFNJBLKOGYTSADFPB",
    +        "GD56QGOWI5ZRJAXNLMAH6BQ3MQEXNWI5H6Q57BFMOQZKPMRLPIYK4RC6",
    +        "GAJFU5JCWRVJ5G7HDYSW6NMDSS6XTRPCNEULD5MWCKQEIGFFSJMDZXUA",
    +        "GDBDJCWCEHLCCJ74HJUDJIBTZO7JVFCI2IIDLEPL33FHOAKVDCNAUE4P",
    +        "GBMXLD4BEJWWETPCFTO2NI7MFRGGBFZDIOVA7OHHFA5XBTB7YHUQBQME",
    +    ],
    +    "STD": [
    +        "GARF35OFGW2XFHFG764UVO2UTUOSDRVL5DU7RXMM7JJJOSVWKK7GATXU",
    +        "GDORF4CKQ2GDOBXXU7R3EXV3XRN6LFCGNYTHMYXDPZ5NECZ6YZLJGAA2",
    +        "GDTTKKRECHQMYWJWKQ5UTONRMNK54WRN3PB4U7JZAPUHLPI75ALN7ORU",
    +        "GDSKTNDAIBUBGQZXEJ64F3P37T7Y45ZOZQCRZY2I46F4UT66KG4JJSOU",
    +        "GCHUIUY5MOBWOXEKZJEQU2DCUG4WHRXM4KAWCEUQK3NTQGBK5RZ6FQBR",
    +        "GDTFYNE5MKGFL625FNUQUHILILFNNRSRYAAXADFFLMOOF5E6V5FLLSBG",
    +        "GDOSJPACWZ2DWSDNNKCVIKMUL3BNVVV3IERJPAZXM3PJMDNXYJIZFUL3",
    +        "GALQ4TZA6VRBBBBYMM3KSBSXJDLC5A7YIGH4SAS6AJ7N4ZA6P6IHWH43",
    +        "GDMMVCANURBLP6O64QWJM3L2EZTDSGTFL4B2BNXKAQPWYDX6WNAFNWK4",
    +    ],
    +}
    +
    +DATA_ENTRY_KEY = "tft-vesting"
    +
    +VESTING_SCHEME = "month1=05/2021,48months,priceunlock=tftvalue>month*0.015+0.15"
    +
    +
    +def _is_valid_cleanup_transaction(
    +    vesting_account_id: str, preauth_signer: str, network, network_passphrase, _get_unlockhash_transaction
    +) -> bool:
    +
    +    unlock_tx = _get_unlockhash_transaction(unlockhash=preauth_signer)
    +    if not unlock_tx:
    +        return False
    +    txe = stellar_sdk.TransactionEnvelope.from_xdr(unlock_tx["transaction_xdr"], network_passphrase)
    +    tx = txe.transaction
    +    if not type(tx.source) is stellar_sdk.keypair.Keypair:
    +        return False
    +    if tx.source.public_key != vesting_account_id:
    +        return False
    +    if len(tx.operations) != 3:
    +        return False
    +    change_trust_op = tx.operations[0]
    +    if not type(change_trust_op) is stellar_sdk.operation.change_trust.ChangeTrust:
    +        return False
    +    if not change_trust_op.source is None:
    +        return False
    +    if int(change_trust_op.limit) != 0:
    +        return False
    +    if change_trust_op.asset.code != "TFT" or change_trust_op.asset.issuer != _TFT_ISSUERS[network]:
    +        return False
    +    manage_data_op = tx.operations[1]
    +    if not type(manage_data_op) is stellar_sdk.operation.manage_data.ManageData:
    +        return False
    +    if not manage_data_op.source is None:
    +        return False
    +    if manage_data_op.data_name != DATA_ENTRY_KEY:
    +        return False
    +    if not manage_data_op.data_value is None:
    +        return False
    +    account_merge_op = tx.operations[2]
    +    if not type(account_merge_op) is stellar_sdk.operation.account_merge.AccountMerge:
    +        return False
    +    if not account_merge_op.source is None:
    +        return False
    +    if account_merge_op.destination != _VESTING_ACTIVATORS[network]:
    +        return False
    +    return True
    +
    +
    +def _verify_signers(
    +    account_record, owner_address: str, network, network_passphrase, _get_unlockhash_transaction
    +) -> bool:
    +    # verify tresholds
    +    tresholds = account_record["thresholds"]
    +    if tresholds["low_threshold"] != 10 or tresholds["med_threshold"] != 10 or tresholds["high_threshold"] != 10:
    +        return False
    +    ## signers found for account
    +    signers = {signer["key"]: (signer["weight"], signer["type"]) for signer in account_record["signers"]}
    +    # example of signers item --> {'GCWPSLTHDH3OYH226EVCLOG33NOMDEO4KUPZQXTU7AWQNJPQPBGTLAVM':(5,'ed25519_public_key')}
    +
    +    # Check all cosigners are in account signers with weight 1
    +    for correct_signer in _COSIGNERS[network]:
    +        if (
    +            correct_signer in signers.keys()
    +            and signers[correct_signer][0] == 1
    +            and signers[correct_signer][1] == "ed25519_public_key"
    +        ):
    +            continue
    +        else:
    +            return False
    +
    +    account_id = account_record["account_id"]
    +
    +    cleanup_signer_correct = False
    +    master_key_weight_correct = False
    +    owner_key_weight_correct = False
    +    for signer in account_record["signers"]:
    +        if signer["type"] == "preauth_tx":
    +            if signer["weight"] != 10:
    +                return False
    +            cleanup_signer_correct = _is_valid_cleanup_transaction(
    +                account_id, signer["key"], network, network_passphrase, _get_unlockhash_transaction
    +            )
    +            continue
    +        if signer["type"] != "ed25519_public_key":
    +            return False
    +        if signer["key"] == account_id:
    +            master_key_weight_correct = signer["weight"] == 0
    +        if signer["key"] == owner_address:
    +            owner_key_weight_correct = signer["weight"] == 5
    +
    +    if len(account_record["signers"]) == 12:
    +        return cleanup_signer_correct and master_key_weight_correct and owner_key_weight_correct
    +    return len(account_record["signers"]) == 11 and master_key_weight_correct and owner_key_weight_correct
    +
    +
    +def is_vesting_account(
    +    account_record: dict, owner_address: str, network, network_passphrase, _get_unlockhash_transaction
    +) -> bool:
    +    if DATA_ENTRY_KEY in account_record.get("data"):
    +        decoded_data = j.data.serializers.base64.decode(account_record["data"][DATA_ENTRY_KEY]).decode()
    +        if decoded_data == VESTING_SCHEME:
    +            if _verify_signers(account_record, owner_address, network, network_passphrase, _get_unlockhash_transaction):
    +                return True
    +    return False
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def is_vesting_account(account_record: dict, owner_address: str, network, network_passphrase, _get_unlockhash_transaction) ‑> bool +
    +
    +
    +
    + +Expand source code + +
    def is_vesting_account(
    +    account_record: dict, owner_address: str, network, network_passphrase, _get_unlockhash_transaction
    +) -> bool:
    +    if DATA_ENTRY_KEY in account_record.get("data"):
    +        decoded_data = j.data.serializers.base64.decode(account_record["data"][DATA_ENTRY_KEY]).decode()
    +        if decoded_data == VESTING_SCHEME:
    +            if _verify_signers(account_record, owner_address, network, network_passphrase, _get_unlockhash_transaction):
    +                return True
    +    return False
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/stellar/wrapped.html b/docs/api/jumpscale/clients/stellar/wrapped.html new file mode 100644 index 000000000..3be18fb48 --- /dev/null +++ b/docs/api/jumpscale/clients/stellar/wrapped.html @@ -0,0 +1,303 @@ + + + + + + +jumpscale.clients.stellar.wrapped API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.stellar.wrapped

    +
    +
    +
    + +Expand source code + +
    # some wrapped stellar_sdk objects to modify the behavior
    +
    +import stellar_sdk
    +from stellar_sdk import Account as stellarAccount
    +from stellar_sdk import Server as stellarServer
    +import time
    +from .exceptions import NoTrustLine, TooLate, UnAuthorized
    +
    +
    +class Account(stellarAccount):
    +    def __init__(self, account_id: str, sequence: int, wallet) -> None:
    +        stellarAccount.__init__(self, account_id, sequence)
    +        self.wallet = wallet
    +
    +    def increment_sequence_number(self):
    +        """
    +        Increments sequence number in this object by one.
    +        """
    +        stellarAccount.increment_sequence_number(self)
    +        self.wallet.sequence = self.sequence
    +        self.wallet.sequencedate = int(time.time())
    +
    +    @property
    +    def last_created_sequence_is_used(self):
    +        return self.wallet.sequence <= self.sequence
    +
    +
    +class Server(stellarServer):
    +    def __init__(self, horizon_url):
    +        super().__init__(horizon_url)
    +
    +    def submit_transaction(self, transaction_envelope):
    +        try:
    +            return super().submit_transaction(transaction_envelope)
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            if e.status == 400:
    +                resultcodes = e.extras["result_codes"]
    +                if resultcodes["transaction"] == "tx_too_late":
    +                    raise TooLate()
    +                if resultcodes["transaction"] == "tx_bad_auth":
    +                    raise UnAuthorized(e.extras["envelope_xdr"])
    +                if resultcodes["transaction"] == "tx_failed":
    +                    if "op_no_trust" in resultcodes["operations"]:
    +                        raise NoTrustLine()
    +                    if "op_bad_auth" in resultcodes["operations"]:
    +                        raise UnAuthorized(e.extras["envelope_xdr"])
    +            raise e
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Account +(account_id: str, sequence: int, wallet) +
    +
    +

    The :class:Account object represents a single +account on the Stellar network and its sequence number.

    +

    Account tracks the sequence number as it is used +by :class:TransactionBuilder <stellar_sdk.transaction_builder.TransactionBuilder>.

    +

    Normally, you can get an :class:Account instance through :func:stellar_sdk.server.Server.load_account +or :func:stellar_sdk.server_async.ServerAsync.load_account.

    +

    An example::

    +
    from stellar_sdk import Keypair, Server
    +
    +server = Server(horizon_url="https://horizon-testnet.stellar.org")
    +source = Keypair.from_secret("SBFZCHU5645DOKRWYBXVOXY2ELGJKFRX6VGGPRYUWHQ7PMXXJNDZFMKD")
    +# <code>account</code> can also be a muxed account
    +source_account = server.load_account(account=source.public_key)
    +
    +

    See Accounts <https://developers.stellar.org/docs/glossary/accounts/>__ for +more information.

    +

    :param account: Account Id of the +account (ex. "GB3KJPLFUYN5VL6R3GU3EGCGVCKFDSD7BEDX42HWG5BWFKB3KQGJJRMA") +or muxed account (ex. "MBZSQ3YZMZEWL5ZRCEQ5CCSOTXCFCMKDGFFP4IEQN2KN6LCHCLI46AAAAAAAAAAE2L2QE") +:param sequence: Current sequence number of the account. +:param raw_data: Raw horizon response data.

    +
    + +Expand source code + +
    class Account(stellarAccount):
    +    def __init__(self, account_id: str, sequence: int, wallet) -> None:
    +        stellarAccount.__init__(self, account_id, sequence)
    +        self.wallet = wallet
    +
    +    def increment_sequence_number(self):
    +        """
    +        Increments sequence number in this object by one.
    +        """
    +        stellarAccount.increment_sequence_number(self)
    +        self.wallet.sequence = self.sequence
    +        self.wallet.sequencedate = int(time.time())
    +
    +    @property
    +    def last_created_sequence_is_used(self):
    +        return self.wallet.sequence <= self.sequence
    +
    +

    Ancestors

    +
      +
    • stellar_sdk.account.Account
    • +
    +

    Instance variables

    +
    +
    var last_created_sequence_is_used
    +
    +
    +
    + +Expand source code + +
    @property
    +def last_created_sequence_is_used(self):
    +    return self.wallet.sequence <= self.sequence
    +
    +
    +
    +

    Methods

    +
    +
    +def increment_sequence_number(self) +
    +
    +

    Increments sequence number in this object by one.

    +
    + +Expand source code + +
    def increment_sequence_number(self):
    +    """
    +    Increments sequence number in this object by one.
    +    """
    +    stellarAccount.increment_sequence_number(self)
    +    self.wallet.sequence = self.sequence
    +    self.wallet.sequencedate = int(time.time())
    +
    +
    +
    +
    +
    +class Server +(horizon_url) +
    +
    +

    Server handles the network connection to a Horizon <https://developers.stellar.org/api/introduction/>_ +instance and exposes an interface for requests to that instance.

    +

    An example::

    +
    from stellar_sdk import Server
    +
    +server = Server("https://horizon-testnet.stellar.org")
    +resp = server.transactions().limit(10).order(desc=True).call()
    +print(resp)
    +
    +

    :param horizon_url: Horizon Server URL +(ex. "https://horizon-testnet.stellar.org" for test network, +"https://horizon.stellar.org" for public network) +:param client: Http client used to send the request

    +
    + +Expand source code + +
    class Server(stellarServer):
    +    def __init__(self, horizon_url):
    +        super().__init__(horizon_url)
    +
    +    def submit_transaction(self, transaction_envelope):
    +        try:
    +            return super().submit_transaction(transaction_envelope)
    +        except stellar_sdk.exceptions.BadRequestError as e:
    +            if e.status == 400:
    +                resultcodes = e.extras["result_codes"]
    +                if resultcodes["transaction"] == "tx_too_late":
    +                    raise TooLate()
    +                if resultcodes["transaction"] == "tx_bad_auth":
    +                    raise UnAuthorized(e.extras["envelope_xdr"])
    +                if resultcodes["transaction"] == "tx_failed":
    +                    if "op_no_trust" in resultcodes["operations"]:
    +                        raise NoTrustLine()
    +                    if "op_bad_auth" in resultcodes["operations"]:
    +                        raise UnAuthorized(e.extras["envelope_xdr"])
    +            raise e
    +
    +

    Ancestors

    +
      +
    • stellar_sdk.server.Server
    • +
    • stellar_sdk.base_server.BaseServer
    • +
    +

    Methods

    +
    +
    +def submit_transaction(self, transaction_envelope) +
    +
    +

    Submits a transaction to the network.

    +

    :param transaction_envelope: :class:stellar_sdk.transaction_envelope.TransactionEnvelope object +or base64 encoded xdr +:param skip_memo_required_check: Allow skipping memo +:return: the response from horizon +:raises: +:exc:ConnectionError <stellar_sdk.exceptions.ConnectionError> +:exc:NotFoundError <stellar_sdk.exceptions.NotFoundError> +:exc:BadRequestError <stellar_sdk.exceptions.BadRequestError> +:exc:BadResponseError <stellar_sdk.exceptions.BadResponseError> +:exc:UnknownRequestError <stellar_sdk.exceptions.UnknownRequestError> +:exc:AccountRequiresMemoError <stellar_sdk.sep.exceptions.AccountRequiresMemoError>

    +
    + +Expand source code + +
    def submit_transaction(self, transaction_envelope):
    +    try:
    +        return super().submit_transaction(transaction_envelope)
    +    except stellar_sdk.exceptions.BadRequestError as e:
    +        if e.status == 400:
    +            resultcodes = e.extras["result_codes"]
    +            if resultcodes["transaction"] == "tx_too_late":
    +                raise TooLate()
    +            if resultcodes["transaction"] == "tx_bad_auth":
    +                raise UnAuthorized(e.extras["envelope_xdr"])
    +            if resultcodes["transaction"] == "tx_failed":
    +                if "op_no_trust" in resultcodes["operations"]:
    +                    raise NoTrustLine()
    +                if "op_bad_auth" in resultcodes["operations"]:
    +                    raise UnAuthorized(e.extras["envelope_xdr"])
    +        raise e
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/syncthing/index.html b/docs/api/jumpscale/clients/syncthing/index.html new file mode 100644 index 000000000..77accf697 --- /dev/null +++ b/docs/api/jumpscale/clients/syncthing/index.html @@ -0,0 +1,101 @@ + + + + + + +jumpscale.clients.syncthing API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.syncthing

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .syncthing import SyncthingClient
    +
    +    return StoredFactory(SyncthingClient)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.syncthing.syncthing
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .syncthing import SyncthingClient
    +
    +    return StoredFactory(SyncthingClient)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/syncthing/syncthing.html b/docs/api/jumpscale/clients/syncthing/syncthing.html new file mode 100644 index 000000000..74179ef19 --- /dev/null +++ b/docs/api/jumpscale/clients/syncthing/syncthing.html @@ -0,0 +1,802 @@ + + + + + + +jumpscale.clients.syncthing.syncthing API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.syncthing.syncthing

    +
    +
    +
    + +Expand source code + +
    from jumpscale.clients.base import Client
    +from jumpscale.core.base import fields
    +from jumpscale.loader import j
    +
    +
    +class SyncthingClient(Client):
    +    host = fields.String()
    +    port = fields.Integer()
    +    apikey = fields.String()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.__url = None
    +        self.__session = None
    +        self.__config = None
    +
    +    @property
    +    def url(self):
    +        if not self.__url:
    +            self.__url = f"http://{self.host}:{self.port}/rest"
    +        return self.__url
    +
    +    @property
    +    def config(self):
    +        if not self.__config:
    +            self.__config = self._call("system/config")
    +        return self.__config
    +
    +    @property
    +    def session(self):
    +        if not self.__session:
    +            self.__session = j.tools.http.Session()
    +            self.__session.headers = {
    +                "Content-Type": "application/json",
    +                "User-Agent": "Syncthing Python client",
    +                "X-API-Key": self.apikey,
    +            }
    +        return self.__session
    +
    +    def _call(self, endpoint, method="get", data=None, return_json=True):
    +        method_call = getattr(self.session, method)
    +        response = method_call(f"{self.url}/{endpoint}", json=data)
    +        response.raise_for_status()
    +
    +        if return_json:
    +            return response.json()
    +        else:
    +            return response.content
    +
    +    def restart(self):
    +        self._call("system/restart", "post")
    +        if not j.sals.nettools.wait_connection_test(self.host, self.port, timeout=3):
    +            j.exceptions.Timeout("Server didn't start in 3 seconds")
    +
    +    def get_status(self):
    +        return self._call("system/status")
    +
    +    def get_id(self):
    +        return self.get_status()["myID"]
    +
    +    def reload_config(self):
    +        self.__config = None
    +
    +    def set_config(self, config=None):
    +        self._call("system/config", "post", config or self.config, False)
    +
    +    def get_folders(self):
    +        return self.config["folders"]
    +
    +    def get_devices(self):
    +        return self.config["devices"]
    +
    +    def _get_folder(self, name):
    +        for idx, folder in enumerate(self.get_folders()):
    +            if folder["id"] == name:
    +                return idx
    +
    +    def _get_device(self, name):
    +        for idx, device in enumerate(self.get_devices()):
    +            if device["name"] == name:
    +                return idx
    +
    +    def check_folder(self, name):
    +        return self._get_folder(name) is not None
    +
    +    def check_device(self, name):
    +        return self._get_device(name) is not None
    +
    +    def delete_folder(self, name):
    +        folders = self.get_folders()
    +        idx = self._get_folder(name)
    +        if idx:
    +            folders.pop(idx)
    +            return self.set_config(self.config)
    +
    +    def delete_device(self, name):
    +        devices = self.get_devices()
    +        idx = self._get_device(name)
    +        if idx:
    +            devices.pop(idx)
    +            return self.set_config(self.config)
    +
    +    def add_folder(
    +        self, name, path, ignore_perms=False, read_only=False, rescan_intervals=10, devices=None, overwrite=False,
    +    ):
    +        folders = self.get_folders()
    +        idx = self._get_folder(name)
    +        if idx:
    +            if overwrite:
    +                folders.pop(idx)
    +            else:
    +                raise j.exceptions.Input(f"Folder with name: {name} already exists")
    +        devices = devices or []
    +        my_id = self.get_id()
    +        if my_id not in devices:
    +            devices.append(my_id)
    +        devices = [{"deviceID": device} for device in devices]
    +        folder = {
    +            "autoNormalize": False,
    +            "copiers": 0,
    +            "devices": devices,
    +            "hashers": 0,
    +            "id": name,
    +            "ignoreDelete": False,
    +            "ignorePerms": ignore_perms,
    +            "invalid": "",
    +            "minDiskFreePct": 5,
    +            "order": "random",
    +            "path": path,
    +            "pullers": 0,
    +            "readOnly": read_only,
    +            "rescanIntervalS": rescan_intervals,
    +            "versioning": {"params": {}, "type": ""},
    +        }
    +        folders.append(folder)
    +        return self.set_config(self.config)
    +
    +    def add_device(self, name, device_id, introducer=False, compression="always", overwrite=False):
    +        devices = self.get_devices()
    +        idx = self._get_device(name)
    +        if idx:
    +            if overwrite:
    +                devices.pop(idx)
    +            else:
    +                raise j.exceptions.Input(f"Device with name: {name} already exists")
    +        device = {
    +            "addresses": ["dynamic"],
    +            "certName": "",
    +            "compression": compression,
    +            "deviceID": device_id,
    +            "introducer": introducer,
    +            "name": name,
    +        }
    +        devices.append(device)
    +        return self.set_config(self.config)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SyncthingClient +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class SyncthingClient(Client):
    +    host = fields.String()
    +    port = fields.Integer()
    +    apikey = fields.String()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.__url = None
    +        self.__session = None
    +        self.__config = None
    +
    +    @property
    +    def url(self):
    +        if not self.__url:
    +            self.__url = f"http://{self.host}:{self.port}/rest"
    +        return self.__url
    +
    +    @property
    +    def config(self):
    +        if not self.__config:
    +            self.__config = self._call("system/config")
    +        return self.__config
    +
    +    @property
    +    def session(self):
    +        if not self.__session:
    +            self.__session = j.tools.http.Session()
    +            self.__session.headers = {
    +                "Content-Type": "application/json",
    +                "User-Agent": "Syncthing Python client",
    +                "X-API-Key": self.apikey,
    +            }
    +        return self.__session
    +
    +    def _call(self, endpoint, method="get", data=None, return_json=True):
    +        method_call = getattr(self.session, method)
    +        response = method_call(f"{self.url}/{endpoint}", json=data)
    +        response.raise_for_status()
    +
    +        if return_json:
    +            return response.json()
    +        else:
    +            return response.content
    +
    +    def restart(self):
    +        self._call("system/restart", "post")
    +        if not j.sals.nettools.wait_connection_test(self.host, self.port, timeout=3):
    +            j.exceptions.Timeout("Server didn't start in 3 seconds")
    +
    +    def get_status(self):
    +        return self._call("system/status")
    +
    +    def get_id(self):
    +        return self.get_status()["myID"]
    +
    +    def reload_config(self):
    +        self.__config = None
    +
    +    def set_config(self, config=None):
    +        self._call("system/config", "post", config or self.config, False)
    +
    +    def get_folders(self):
    +        return self.config["folders"]
    +
    +    def get_devices(self):
    +        return self.config["devices"]
    +
    +    def _get_folder(self, name):
    +        for idx, folder in enumerate(self.get_folders()):
    +            if folder["id"] == name:
    +                return idx
    +
    +    def _get_device(self, name):
    +        for idx, device in enumerate(self.get_devices()):
    +            if device["name"] == name:
    +                return idx
    +
    +    def check_folder(self, name):
    +        return self._get_folder(name) is not None
    +
    +    def check_device(self, name):
    +        return self._get_device(name) is not None
    +
    +    def delete_folder(self, name):
    +        folders = self.get_folders()
    +        idx = self._get_folder(name)
    +        if idx:
    +            folders.pop(idx)
    +            return self.set_config(self.config)
    +
    +    def delete_device(self, name):
    +        devices = self.get_devices()
    +        idx = self._get_device(name)
    +        if idx:
    +            devices.pop(idx)
    +            return self.set_config(self.config)
    +
    +    def add_folder(
    +        self, name, path, ignore_perms=False, read_only=False, rescan_intervals=10, devices=None, overwrite=False,
    +    ):
    +        folders = self.get_folders()
    +        idx = self._get_folder(name)
    +        if idx:
    +            if overwrite:
    +                folders.pop(idx)
    +            else:
    +                raise j.exceptions.Input(f"Folder with name: {name} already exists")
    +        devices = devices or []
    +        my_id = self.get_id()
    +        if my_id not in devices:
    +            devices.append(my_id)
    +        devices = [{"deviceID": device} for device in devices]
    +        folder = {
    +            "autoNormalize": False,
    +            "copiers": 0,
    +            "devices": devices,
    +            "hashers": 0,
    +            "id": name,
    +            "ignoreDelete": False,
    +            "ignorePerms": ignore_perms,
    +            "invalid": "",
    +            "minDiskFreePct": 5,
    +            "order": "random",
    +            "path": path,
    +            "pullers": 0,
    +            "readOnly": read_only,
    +            "rescanIntervalS": rescan_intervals,
    +            "versioning": {"params": {}, "type": ""},
    +        }
    +        folders.append(folder)
    +        return self.set_config(self.config)
    +
    +    def add_device(self, name, device_id, introducer=False, compression="always", overwrite=False):
    +        devices = self.get_devices()
    +        idx = self._get_device(name)
    +        if idx:
    +            if overwrite:
    +                devices.pop(idx)
    +            else:
    +                raise j.exceptions.Input(f"Device with name: {name} already exists")
    +        device = {
    +            "addresses": ["dynamic"],
    +            "certName": "",
    +            "compression": compression,
    +            "deviceID": device_id,
    +            "introducer": introducer,
    +            "name": name,
    +        }
    +        devices.append(device)
    +        return self.set_config(self.config)
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var apikey
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var config
    +
    +
    +
    + +Expand source code + +
    @property
    +def config(self):
    +    if not self.__config:
    +        self.__config = self._call("system/config")
    +    return self.__config
    +
    +
    +
    var host
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var port
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var session
    +
    +
    +
    + +Expand source code + +
    @property
    +def session(self):
    +    if not self.__session:
    +        self.__session = j.tools.http.Session()
    +        self.__session.headers = {
    +            "Content-Type": "application/json",
    +            "User-Agent": "Syncthing Python client",
    +            "X-API-Key": self.apikey,
    +        }
    +    return self.__session
    +
    +
    +
    var url
    +
    +
    +
    + +Expand source code + +
    @property
    +def url(self):
    +    if not self.__url:
    +        self.__url = f"http://{self.host}:{self.port}/rest"
    +    return self.__url
    +
    +
    +
    +

    Methods

    +
    +
    +def add_device(self, name, device_id, introducer=False, compression='always', overwrite=False) +
    +
    +
    +
    + +Expand source code + +
    def add_device(self, name, device_id, introducer=False, compression="always", overwrite=False):
    +    devices = self.get_devices()
    +    idx = self._get_device(name)
    +    if idx:
    +        if overwrite:
    +            devices.pop(idx)
    +        else:
    +            raise j.exceptions.Input(f"Device with name: {name} already exists")
    +    device = {
    +        "addresses": ["dynamic"],
    +        "certName": "",
    +        "compression": compression,
    +        "deviceID": device_id,
    +        "introducer": introducer,
    +        "name": name,
    +    }
    +    devices.append(device)
    +    return self.set_config(self.config)
    +
    +
    +
    +def add_folder(self, name, path, ignore_perms=False, read_only=False, rescan_intervals=10, devices=None, overwrite=False) +
    +
    +
    +
    + +Expand source code + +
    def add_folder(
    +    self, name, path, ignore_perms=False, read_only=False, rescan_intervals=10, devices=None, overwrite=False,
    +):
    +    folders = self.get_folders()
    +    idx = self._get_folder(name)
    +    if idx:
    +        if overwrite:
    +            folders.pop(idx)
    +        else:
    +            raise j.exceptions.Input(f"Folder with name: {name} already exists")
    +    devices = devices or []
    +    my_id = self.get_id()
    +    if my_id not in devices:
    +        devices.append(my_id)
    +    devices = [{"deviceID": device} for device in devices]
    +    folder = {
    +        "autoNormalize": False,
    +        "copiers": 0,
    +        "devices": devices,
    +        "hashers": 0,
    +        "id": name,
    +        "ignoreDelete": False,
    +        "ignorePerms": ignore_perms,
    +        "invalid": "",
    +        "minDiskFreePct": 5,
    +        "order": "random",
    +        "path": path,
    +        "pullers": 0,
    +        "readOnly": read_only,
    +        "rescanIntervalS": rescan_intervals,
    +        "versioning": {"params": {}, "type": ""},
    +    }
    +    folders.append(folder)
    +    return self.set_config(self.config)
    +
    +
    +
    +def check_device(self, name) +
    +
    +
    +
    + +Expand source code + +
    def check_device(self, name):
    +    return self._get_device(name) is not None
    +
    +
    +
    +def check_folder(self, name) +
    +
    +
    +
    + +Expand source code + +
    def check_folder(self, name):
    +    return self._get_folder(name) is not None
    +
    +
    +
    +def delete_device(self, name) +
    +
    +
    +
    + +Expand source code + +
    def delete_device(self, name):
    +    devices = self.get_devices()
    +    idx = self._get_device(name)
    +    if idx:
    +        devices.pop(idx)
    +        return self.set_config(self.config)
    +
    +
    +
    +def delete_folder(self, name) +
    +
    +
    +
    + +Expand source code + +
    def delete_folder(self, name):
    +    folders = self.get_folders()
    +    idx = self._get_folder(name)
    +    if idx:
    +        folders.pop(idx)
    +        return self.set_config(self.config)
    +
    +
    +
    +def get_devices(self) +
    +
    +
    +
    + +Expand source code + +
    def get_devices(self):
    +    return self.config["devices"]
    +
    +
    +
    +def get_folders(self) +
    +
    +
    +
    + +Expand source code + +
    def get_folders(self):
    +    return self.config["folders"]
    +
    +
    +
    +def get_id(self) +
    +
    +
    +
    + +Expand source code + +
    def get_id(self):
    +    return self.get_status()["myID"]
    +
    +
    +
    +def get_status(self) +
    +
    +
    +
    + +Expand source code + +
    def get_status(self):
    +    return self._call("system/status")
    +
    +
    +
    +def reload_config(self) +
    +
    +
    +
    + +Expand source code + +
    def reload_config(self):
    +    self.__config = None
    +
    +
    +
    +def restart(self) +
    +
    +
    +
    + +Expand source code + +
    def restart(self):
    +    self._call("system/restart", "post")
    +    if not j.sals.nettools.wait_connection_test(self.host, self.port, timeout=3):
    +        j.exceptions.Timeout("Server didn't start in 3 seconds")
    +
    +
    +
    +def set_config(self, config=None) +
    +
    +
    +
    + +Expand source code + +
    def set_config(self, config=None):
    +    self._call("system/config", "post", config or self.config, False)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/taiga/index.html b/docs/api/jumpscale/clients/taiga/index.html new file mode 100644 index 000000000..fb8c2237a --- /dev/null +++ b/docs/api/jumpscale/clients/taiga/index.html @@ -0,0 +1,106 @@ + + + + + + +jumpscale.clients.taiga API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.taiga

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .taiga import TaigaClient
    +
    +    return StoredFactory(TaigaClient)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.taiga.models
    +
    +
    +
    +
    jumpscale.clients.taiga.taiga
    +
    +

    Taiga client …

    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .taiga import TaigaClient
    +
    +    return StoredFactory(TaigaClient)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/taiga/models.html b/docs/api/jumpscale/clients/taiga/models.html new file mode 100644 index 000000000..432a4e9a0 --- /dev/null +++ b/docs/api/jumpscale/clients/taiga/models.html @@ -0,0 +1,3085 @@ + + + + + + +jumpscale.clients.taiga.models API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.taiga.models

    +
    +
    +
    + +Expand source code + +
    from functools import lru_cache
    +from textwrap import dedent
    +
    +from jumpscale.loader import j
    +import yaml
    +
    +
    +class CircleResource:
    +    def __init__(self, taigaclient, original_object):
    +        self._original_object = original_object
    +        self._client = taigaclient
    +        self._api = self._client.api
    +
    +    def check_by_name(self, list_obj, name_to_find):
    +        """Check if list contains name, used to check priorities, severities, statuses, types and etc
    +            and return the equivalent id.
    +
    +        Args:
    +            list_obj (list): list of objects that we need to search into
    +            name_to_find (str): word to search for in the list
    +
    +        Returns:
    +            int: Id of the matched object if found, or id of the first object in the list.
    +        """
    +        for obj in list_obj:
    +            if obj.name == name_to_find:
    +                return obj.id
    +
    +        return list_obj[0].id
    +
    +
    +class CircleIssue(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<Issue {self._original_object}>"
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/project/despiegk-tftech-software/issue/55
    +        return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/issue/{self.ref}"
    +
    +    @property
    +    def as_md(self):
    +        TEMPLATE = dedent(
    +            """
    +            - **Subject:** [{{issue.subject}}]({{issue.url}})
    +            - **Created Date:** {{issue.created_date or 'unknown' }}
    +            - **Due Date:** {{issue.due_date or 'unknown' }}
    +            - **Owner Name:** {{issue.owner_extra_info.get('username', 'unknown')}}
    +            - **Owner Email:** {{issue.owner_extra_info.get('email', 'unknown')}}
    +            - **Project:** {{issue.project_extra_info.get('name', 'unknown')}}
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, issue=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        if self.assigned_to_extra_info is not None:
    +            obj["assigned_to"] = {
    +                "username": self.assigned_to_extra_info.get("username"),
    +                "id": self.assigned_to,
    +                "email": self.assigned_to_extra_info.get("email"),
    +            }
    +        obj["basic_info"] = {
    +            "subject": self.subject,
    +            "id": self.id,
    +            "description": self.description if hasattr(self, "description") else "",
    +            "tags": self.tags,
    +            "version": self.version,
    +            "url": self.url,
    +        }
    +        obj["project"] = {
    +            "name": self.project_extra_info.get("name"),
    +            "id": self.project,
    +        }
    +        obj["status"] = {
    +            "name": self.status_extra_info.get("name"),
    +            "id": self.status,
    +        }
    +        obj["severity"] = {
    +            "name": self._client.api.severities.get(self.severity).name,
    +            "id": self.severity,
    +        }
    +        obj["priority"] = {
    +            "name": self._client.api.priorities.get(self.priority).name,
    +            "id": self.priority,
    +        }
    +        obj["type"] = {
    +            "name": self._client.api.issue_types.get(self.type).name,
    +            "id": self.type,
    +        }
    +        obj["date"] = {
    +            "created_date": self.created_date,
    +            "modified_date": self.modified_date,
    +            "due_date": self.due_date,
    +            "due_date_reason": self.due_date_reason,
    +            "due_date_status": self.due_date_status,
    +            "finished_date": self.finished_date,
    +        }
    +        owner = {
    +            "username": self.owner_extra_info["username"],
    +            "id": self.owner,
    +            "email": self.owner_extra_info["email"],
    +        }
    +        watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +        watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +        obj["membership"] = {"owner": owner, "watchers": watchers}
    +
    +        obj["statistics"] = {
    +            "total_voters": self.total_voters,
    +            "total_watchers": self.total_watchers,
    +        }
    +        obj["custom_fields"] = self._client.get_issue_custom_fields(self.id)
    +        obj["additional_info"] = {
    +            "is_blocked": self.is_blocked,
    +            "is_closed": self.is_closed,
    +            "is_voter": self.is_voter,
    +            "is_watcher": self.is_watcher,
    +            "blocked_note": self.blocked_note,
    +        }
    +        return yaml.dump(obj)
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + ["as_yaml", "url", "as_md"]
    +
    +
    +class CircleStory(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<Story {self._original_object}>"
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/project/despiegk-tftech-software/us/4
    +        return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/us/{self.ref}"
    +
    +    @property
    +    def circle_tasks(self):
    +        return [CircleTask(self._client, task) for task in self.list_tasks()]
    +
    +    @property
    +    def as_md(self):
    +        TEMPLATE = dedent(
    +            """
    +            - **Subject:** [{{story.subject}}]({{story.url}})
    +            - **Assigned to:** {{story.assigned_to_extra_info and story.assigned_to_extra_info.get('username', 'not assigned') or 'not assigned' }}
    +            - **Watchers:** {{story.watchers or 'no watchers'}}
    +            {% if story.tasks %}
    +            - **Tasks**:
    +                {% for task in story.circle_tasks %}
    +                - [**{{task.subject}}**]({{task.url}})
    +                {% endfor %}
    +            {% endif %}
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, story=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        if self.assigned_to_extra_info is not None:
    +            obj["assigned_to"] = {
    +                "username": self.assigned_to_extra_info.get("username") or None,
    +                "id": self.assigned_to,
    +                "email": self.assigned_to_extra_info.get("email"),
    +            }
    +
    +        obj["basic_info"] = {
    +            "subject": self.subject,
    +            "id": self.id,
    +            "description": self.description if hasattr(self, "description") else "",
    +            "tags": self.tags,
    +            "version": self.version,
    +            "url": self.url,
    +        }
    +        obj["project"] = {
    +            "name": self.project_extra_info.get("name"),
    +            "id": self.project,
    +        }
    +        obj["status"] = {
    +            "name": self.status_extra_info.get("name"),
    +            "id": self.status,
    +        }
    +
    +        obj["date"] = {
    +            "created_date": self.created_date,
    +            "modified_date": self.modified_date,
    +            "due_date": self.due_date,
    +            "due_date_reason": self.due_date_reason,
    +            "due_date_status": self.due_date_status,
    +            "finish_date": self.finish_date,
    +        }
    +        owner = {
    +            "username": self.owner_extra_info["username"],
    +            "id": self.owner,
    +            "email": self.owner_extra_info["email"],
    +        }
    +        watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +        watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +        obj["membership"] = {"owner": owner, "watchers": watchers}
    +        obj["requirements"] = {
    +            "client_requirement": self.client_requirement,
    +            "team_requirement": self.team_requirement,
    +        }
    +        obj["statistics"] = {
    +            "total_attachments": self.total_attachments,
    +            "total_comments": self.total_comments,
    +            "total_voters": self.total_voters,
    +            "total_watchers": self.total_watchers,
    +        }
    +        obj["custom_fields"] = self._client.get_story_custom_fields(self.id)
    +        obj["tasks"] = [task.id for task in self.tasks]
    +        obj["additional_info"] = {
    +            "is_blocked": self.is_blocked,
    +            "is_closed": self.is_closed,
    +            "is_voter": self.is_voter,
    +            "is_watcher": self.is_watcher,
    +            "blocked_note": self.blocked_note,
    +            "generated_from_issue": self.generated_from_issue,
    +            "generated_from_task": self.generated_from_task,
    +        }
    +        return yaml.dump(obj)
    +
    +    @property
    +    def tasks(self):
    +        return self.list_tasks()
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + ["as_yaml", "circle_tasks", "url", "as_md"]
    +
    +
    +class CircleTask(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<Task {self._original_object}>"
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/project/despiegk-tftech-software/task/2
    +        return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/task/{self.ref}"
    +
    +    @property
    +    def as_md(self):
    +        TEMPLATE = dedent(
    +            """
    +            - **Subject:** [{{task.subject}}]({{task.url}})
    +            - **Created Date:** {{task.created_date or 'unknown' }}
    +            - **Due Date:** {{task.due_date or 'unknown' }}
    +            - **Owner Name:** {{task.owner_extra_info.get('username', 'unknown')}}
    +            - **Owner Email:** {{task.owner_extra_info.get('email', 'unknown')}}
    +            - **Project:** {{task.project_extra_info.get('name', 'unknown')}}
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, task=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        if self.assigned_to_extra_info is not None:
    +            obj["assigned_to"] = {
    +                "username": self.assigned_to_extra_info.get("username") or None,
    +                "id": self.assigned_to,
    +                "email": self.assigned_to_extra_info.get("email"),
    +            }
    +
    +        obj["basic_info"] = {
    +            "subject": self.subject,
    +            "id": self.id,
    +            "description": self.description if hasattr(self, "description") else "",
    +            "tags": self.tags,
    +            "version": self.version,
    +            "url": self.url,
    +        }
    +        obj["project"] = {
    +            "name": self.project_extra_info.get("name"),
    +            "id": self.project,
    +        }
    +        obj["status"] = {
    +            "name": self.status_extra_info.get("name"),
    +            "id": self.status,
    +        }
    +        obj["user_story"] = {
    +            "subject": self.user_story_extra_info.get("subject"),
    +            "id": self.user_story,
    +        }
    +        obj["date"] = {
    +            "created_date": self.created_date,
    +            "modified_date": self.modified_date,
    +            "due_date": self.due_date,
    +            "due_date_reason": self.due_date_reason,
    +            "due_date_status": self.due_date_status,
    +            "finished_date": self.finished_date,
    +        }
    +        owner = {
    +            "username": self.owner_extra_info["username"],
    +            "id": self.owner,
    +            "email": self.owner_extra_info["email"],
    +        }
    +        watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +        watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +        obj["membership"] = {"owner": owner, "watchers": watchers}
    +        obj["statistics"] = {
    +            "total_comments": self.total_comments,
    +            "total_voters": self.total_voters,
    +            "total_watchers": self.total_watchers,
    +        }
    +        obj["additional_info"] = {
    +            "is_blocked": self.is_blocked,
    +            "is_closed": self.is_closed,
    +            "is_voter": self.is_voter,
    +            "is_watcher": self.is_watcher,
    +            "blocked_note": self.blocked_note,
    +        }
    +        return yaml.dump(obj)
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + ["as_yaml", "url", "as_md"]
    +
    +
    +class CircleUser(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<User {self._original_object}>"
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/profile/ahartl
    +        return f"{self._client.host}/profile/{self.username}"
    +
    +    @property
    +    def clean_name(self):
    +        return self.username.lower()
    +
    +    def get_stories(self):
    +        return self._client.list_all_user_stories(self._original_object.username)
    +
    +    def get_issues(self):
    +        return self._client.list_all_issues(self._original_object.username)
    +
    +    def get_circles(self):
    +        circles = []
    +        all_circles = self._client.get_user_circles(self._original_object.username)
    +        for c in all_circles:
    +            cname = c.name.lower()
    +            if cname.startswith("team"):
    +                circles.append(TeamCircle(self._client, c))
    +            elif cname.startswith("funnel"):
    +                circles.append(FunnelCircle(self._client, c))
    +            elif cname.startswith("project"):
    +                circles.append(ProjectCircle(self._client, c))
    +            elif cname.startswith("archive"):
    +                pass  # to not included in as_md property
    +            else:
    +                circles.append(Circle(self._client, c))
    +        return circles
    +
    +    def get_tasks(self):
    +        return self._client.list_all_tasks(self._original_object.username)
    +
    +    @property
    +    def stories(self):
    +        res = []
    +        for s in self.get_stories():
    +            res.append(CircleStory(self._client, s))
    +
    +        return res
    +
    +    @property
    +    def issues(self):
    +        res = []
    +        for s in self.get_issues():
    +            res.append(CircleIssue(self._client, s))
    +
    +        return res
    +
    +    @property
    +    def circles(self):
    +        return self.get_circles()
    +
    +    @property
    +    def tasks(self):
    +        return self.get_tasks()
    +
    +    @property
    +    def as_md(self):
    +
    +        TEMPLATE = dedent(
    +            """
    +            # {{user.username}}
    +
    +            User profile is at: [{{user.url}}]({{user.url}})
    +
    +            {% if user.circles %}
    +            ## Circles
    +
    +            {% for c in user.circles %}
    +
    +            '[{{c.name}}]({{c.clean_name}}.md) [{{c.url}}]({{c.url}})
    +            {% endfor %}
    +
    +            {% endif %}
    +
    +            {% if user.stories %}
    +            ## User stories
    +            {% for story in user.stories %}
    +
    +            ### {{story.subject}}
    +
    +            {{story.as_md}}
    +
    +            {% endfor %}
    +
    +            {% endif %}
    +
    +
    +            {% if user.issues %}
    +            ## Issues
    +
    +            {% for issue in user.issues %}
    +            ### {{issue.subject}}
    +
    +            {{issue.as_md}}
    +
    +            {% endfor %}
    +            {% endif %}
    +
    +            {% if user.tasks %}
    +            ## Tasks
    +
    +            {% for task in user.tasks %}
    +            ### {{task.subject}}
    +
    +            {{task.as_md}}
    +
    +            {% endfor %}
    +            {% endif %}
    +
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, user=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        obj["basic_info"] = {
    +            "username": self.username,
    +            "email": self.email,
    +            "id": self.id,
    +            "full_name": self.full_name,
    +            "bio": self.bio,
    +            "lang": self.lang,
    +            "public_key": self.public_key,
    +            "photo": self.photo,
    +            "url": self.url,
    +        }
    +        obj["date"] = {"date_joined": self.date_joined, "timezone": self.timezone}
    +        obj["statistics"] = {
    +            "total_private_projects": self.total_private_projects,
    +            "total_public_projects": self.total_public_projects,
    +        }
    +        obj["roles"] = self.roles
    +        obj["limits"] = {
    +            "max_memberships_private_projects": self.max_memberships_private_projects,
    +            "max_memberships_public_projects": self.max_memberships_public_projects,
    +            "max_private_projects": self.max_private_projects,
    +            "max_public_projects": self.max_memberships_public_projects,
    +        }
    +        obj["terms"] = {
    +            "accepted_terms": self.accepted_terms,
    +            "read_new_terms": self.read_new_terms,
    +        }
    +        return yaml.dump(obj)
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + [
    +            "as_yaml",
    +            "as_md",
    +            "issues",
    +            "stories",
    +            "tasks",
    +            "get_circles",
    +            "get_issues",
    +            "get_stories",
    +            "get_tasks",
    +            "url",
    +            "clean_name",
    +        ]
    +
    +
    +class Circle(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    @property
    +    def clean_name(self):
    +        return self.slug.lower().replace("-", "_")
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/project/despiegk-tftech-software
    +        return f"{self._client.host}/project/{self.slug}"
    +
    +    @lru_cache(maxsize=128)
    +    def get_project_info(self):
    +
    +        prios = list(map(lambda x: (str(x), x.id), self._original_object.list_priorities()))
    +        severities = list(map(lambda x: (str(x), x.id), self._original_object.list_severities()))
    +        statuses = list(map(lambda x: (str(x), x.id), self._original_object.list_issue_statuses()))
    +        issues_types = list(map(lambda x: (str(x), x.id), self._original_object.list_issue_types()))
    +
    +        return f"prios: {prios}, severities: {severities}, issues_types: {issues_types}, statuses: {statuses}"
    +
    +    def create_issue(self, subject, prio=None, status=None, issue_type=None, severity=None):
    +        prio = prio or self._original_object.list_priorities()[0].id
    +        status = status or self._original_object.list_issue_statuses()[0].id
    +        severity = severity or self._original_object.list_severities()[0].id
    +        issue_type = issue_type or self._original_object.list_issue_types()[0].id
    +
    +        return self._original_object.add_issue(subject, prio, status, issue_type, severity)
    +
    +    def copy_issue(self, issue, project):
    +        """Copy issue to specific project
    +
    +        Args:
    +            issue (int , CircleIssue): issue or issue id to be copied
    +            to_project (int, Circle, FunnelCircle, ProjectCircle, TeamCircle):  circle or circle id to copy issue into
    +
    +        Returns:
    +            Issue: created issue object
    +        """
    +        issue_id = issue
    +        issue_obj = issue
    +        project_id = project
    +        project_obj = project
    +        if not isinstance(issue, int):
    +            issue_id = issue.id
    +            issue_obj = issue
    +        else:
    +            issue_obj = self._client.api.issues.get(issue_id)
    +
    +        if not isinstance(project, int):
    +            project_id = project.id
    +            project_obj = project
    +        else:
    +            project_obj = Circle(self._client, self._client.api.projects.get(project_id))
    +
    +        issue_priority = self.check_by_name(
    +            project_obj.priorities, self._client.api.priorities.get(issue_obj.priority).name
    +        )
    +        issue_type = self.check_by_name(
    +            project_obj.list_issue_types(), self._client.api.issue_types.get(issue_obj.type).name
    +        )
    +        issue_severity = self.check_by_name(
    +            project_obj.list_severities(), self._client.api.severities.get(issue_obj.severity).name
    +        )
    +        issue_status = self.check_by_name(project_obj.list_issue_statuses(), issue_obj.status_extra_info["name"])
    +        issue_custom_field = self._client.get_issue_custom_fields(issue_id)
    +
    +        created = project_obj.add_issue(
    +            issue_obj.subject,
    +            issue_priority,
    +            issue_status,
    +            issue_type,
    +            issue_severity,
    +            description=issue_obj.description,
    +            watchers=issue_obj.watchers,
    +            assigned_to=issue_obj.assigned_to,
    +        )
    +
    +        for field in issue_custom_field:
    +            is_found = False
    +            new_attr = None
    +            for attr in project_obj.list_issue_attributes():
    +                if attr.name == field["name"] and not is_found:
    +                    created.set_attribute(attr.id, field["value"])
    +                    is_found = True
    +
    +            if not is_found:
    +                new_attr = project_obj.add_issue_attribute(field["name"])
    +                created.set_attribute(new_attr.id, field["value"])
    +
    +        return created
    +
    +    def move_issue(self, issue, project):
    +        """Move issue to specific project
    +        HINT: Move will delete the issue from original circle
    +
    +        Args:
    +            issue (int , CircleIssue): issue or issue id to be movied
    +            to_project (int, Circle, FunnelCircle, ProjectCircle, TeamCircle):  circle or circle id to move issue into
    +
    +        Returns:
    +            Issue: created issue object
    +        """
    +        issue_id = issue
    +        issue_obj = issue
    +        project_id = project
    +        project_obj = project
    +        if not isinstance(issue, int):
    +            issue_id = issue.id
    +            issue_obj = issue
    +        else:
    +            issue_obj = self._client.api.issues.get(issue_id)
    +
    +        if not isinstance(project, int):
    +            project_id = project.id
    +            project_obj = project
    +        else:
    +            project_obj = Circle(self._client, self._client.api.projects.get(project_id))
    +
    +        created = self.copy_issue(issue_obj, project_obj)
    +
    +        if created:
    +            issue_obj.delete()
    +        return created
    +
    +    def create_story(self, subject="", **attrs):
    +        return self._original_object.add_user_story(subject, **attrs)
    +
    +    @property
    +    def stories(self):
    +        res = []
    +        for s in self._original_object.list_user_stories():
    +            res.append(CircleStory(self._client, s))
    +
    +        return res
    +
    +    @property
    +    def issues(self):
    +        res = []
    +        for s in self._original_object.list_issues():
    +            res.append(CircleIssue(self._client, s))
    +        return res
    +
    +    @property
    +    def circle_users(self):
    +        return self._original_object.members_objects()
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + [
    +            "create_issue",
    +            "create_story",
    +            "move_issue",
    +            "get_project_info",
    +            "as_md",
    +            "issues",
    +            "stories",
    +            "circle_users",
    +            "as_yaml",
    +        ]
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<Circle {self._original_object}>"
    +
    +    @property
    +    def as_md(self):
    +
    +        TEMPLATE = dedent(
    +            """
    +            # Circle {{project.name}}
    +
    +            - Homepage: {{project.url}}
    +            - Modified Date: {{project.modified_date}}
    +
    +            {% if project.stories %}
    +            ## Stories
    +
    +            {% for story in project.stories %}
    +
    +            ### {{story.subject}}
    +            {{story.as_md}}
    +
    +            {% endfor %}
    +
    +            {% endif %}
    +
    +
    +            {% if project.issues %}
    +            ## Issues
    +
    +            {% for issue in project.issues %}
    +
    +            ### {{issue.subject}}
    +
    +            {{issue.as_md}}
    +
    +            {% endfor %}
    +
    +            {% endif %}
    +
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, project=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        obj["basic_info"] = {
    +            "name": self.name,
    +            "slug": self.slug,
    +            "id": self.id,
    +            "description": self.description if hasattr(self, "description") else "",
    +            "tags": self.tags,
    +            "is_private": self.is_private,
    +            "looking_for_people_note": self.looking_for_people_note,
    +            "url": self.url,
    +        }
    +        obj["date"] = {
    +            "created_date": self.created_date,
    +            "modified_date": self.modified_date,
    +        }
    +        obj["modules"] = {
    +            "is_backlog_activated": self.is_backlog_activated,
    +            "is_issues_activated": self.is_issues_activated,
    +            "is_kanban_activated": self.is_kanban_activated,
    +            "is_wiki_activated": self.is_wiki_activated,
    +            "videoconferences": self.videoconferences,
    +        }
    +        owner = {
    +            "username": self.owner["username"],
    +            "id": self.owner["id"],
    +            "email": self.owner["email"],
    +        }
    +        members = [
    +            {"name": member.full_name, "id": member.id, "role": member.role_name,} for member in self.list_memberships()
    +        ]
    +        other_membership = {
    +            "i_am_owner": self.i_am_owner,
    +            "i_am_admin": self.i_am_admin,
    +            "i_am_member": self.i_am_member,
    +        }
    +        obj["membership"] = {
    +            "owner": owner,
    +            "members": members,
    +            "others": other_membership,
    +        }
    +        obj["statistics"] = {
    +            "total_activity": self.total_activity,
    +            "total_fans": self.total_fans,
    +            "total_watchers": self.total_watchers,
    +        }
    +        obj["stories"] = [story.id for story in self.stories]
    +        obj["stories_attributes"] = [st_attr.name for st_attr in self.list_user_story_attributes()]
    +        obj["issues"] = [issue.id for issue in self.issues]
    +        obj["issues_attributes"] = [issue_attr.name for issue_attr in self.list_issue_attributes()]
    +        return yaml.dump(obj)
    +
    +
    +class TeamCircle(Circle):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        try:
    +            return getattr(self._original_object, attr)
    +        except Exception as e:
    +            if hasattr(self._original_object, "_original_object"):
    +                return getattr(self._original_object._original_object, attr)
    +            else:
    +                raise j.exceptions.Runtime(f"{attr} not found in {self} and not in {self._original_object}")
    +
    +    def __str__(self):
    +        return f"<TeamCircle {self._original_object}>"
    +
    +
    +class FunnelCircle(Circle):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        try:
    +            return getattr(self._original_object, attr)
    +        except Exception as e:
    +            if hasattr(self._original_object, "_original_object"):
    +                return getattr(self._original_object._original_object, attr)
    +            else:
    +                raise j.exceptions.Runtime(f"{attr} not found in {self} and not in {self._original_object}")
    +
    +    def __str__(self):
    +        return f"<FunnelCircle {self._original_object}>"
    +
    +
    +class ProjectCircle(Circle):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        try:
    +            return getattr(self._original_object, attr)
    +        except Exception as e:
    +            if hasattr(self._original_object, "_original_object"):
    +                return getattr(self._original_object._original_object, attr)
    +            else:
    +                raise j.exceptions.Runtime(f"{attr} not found in {self} and not in {self._original_object}")
    +
    +    def __str__(self):
    +        return f"<ProjectCircle {self._original_object}>"
    +
    +
    +class ArchiveCircle(Circle):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        try:
    +            return getattr(self._original_object, attr)
    +        except Exception as e:
    +            if hasattr(self._original_object, "_original_object"):
    +                return getattr(self._original_object._original_object, attr)
    +            else:
    +                raise j.exceptions.Runtime(f"{attr} not found in {self} and not in {self._original_object}")
    +
    +    def __str__(self):
    +        return f"<ArchiveCircle {self._original_object}>"
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class ArchiveCircle +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class ArchiveCircle(Circle):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        try:
    +            return getattr(self._original_object, attr)
    +        except Exception as e:
    +            if hasattr(self._original_object, "_original_object"):
    +                return getattr(self._original_object._original_object, attr)
    +            else:
    +                raise j.exceptions.Runtime(f"{attr} not found in {self} and not in {self._original_object}")
    +
    +    def __str__(self):
    +        return f"<ArchiveCircle {self._original_object}>"
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +class Circle +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class Circle(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    @property
    +    def clean_name(self):
    +        return self.slug.lower().replace("-", "_")
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/project/despiegk-tftech-software
    +        return f"{self._client.host}/project/{self.slug}"
    +
    +    @lru_cache(maxsize=128)
    +    def get_project_info(self):
    +
    +        prios = list(map(lambda x: (str(x), x.id), self._original_object.list_priorities()))
    +        severities = list(map(lambda x: (str(x), x.id), self._original_object.list_severities()))
    +        statuses = list(map(lambda x: (str(x), x.id), self._original_object.list_issue_statuses()))
    +        issues_types = list(map(lambda x: (str(x), x.id), self._original_object.list_issue_types()))
    +
    +        return f"prios: {prios}, severities: {severities}, issues_types: {issues_types}, statuses: {statuses}"
    +
    +    def create_issue(self, subject, prio=None, status=None, issue_type=None, severity=None):
    +        prio = prio or self._original_object.list_priorities()[0].id
    +        status = status or self._original_object.list_issue_statuses()[0].id
    +        severity = severity or self._original_object.list_severities()[0].id
    +        issue_type = issue_type or self._original_object.list_issue_types()[0].id
    +
    +        return self._original_object.add_issue(subject, prio, status, issue_type, severity)
    +
    +    def copy_issue(self, issue, project):
    +        """Copy issue to specific project
    +
    +        Args:
    +            issue (int , CircleIssue): issue or issue id to be copied
    +            to_project (int, Circle, FunnelCircle, ProjectCircle, TeamCircle):  circle or circle id to copy issue into
    +
    +        Returns:
    +            Issue: created issue object
    +        """
    +        issue_id = issue
    +        issue_obj = issue
    +        project_id = project
    +        project_obj = project
    +        if not isinstance(issue, int):
    +            issue_id = issue.id
    +            issue_obj = issue
    +        else:
    +            issue_obj = self._client.api.issues.get(issue_id)
    +
    +        if not isinstance(project, int):
    +            project_id = project.id
    +            project_obj = project
    +        else:
    +            project_obj = Circle(self._client, self._client.api.projects.get(project_id))
    +
    +        issue_priority = self.check_by_name(
    +            project_obj.priorities, self._client.api.priorities.get(issue_obj.priority).name
    +        )
    +        issue_type = self.check_by_name(
    +            project_obj.list_issue_types(), self._client.api.issue_types.get(issue_obj.type).name
    +        )
    +        issue_severity = self.check_by_name(
    +            project_obj.list_severities(), self._client.api.severities.get(issue_obj.severity).name
    +        )
    +        issue_status = self.check_by_name(project_obj.list_issue_statuses(), issue_obj.status_extra_info["name"])
    +        issue_custom_field = self._client.get_issue_custom_fields(issue_id)
    +
    +        created = project_obj.add_issue(
    +            issue_obj.subject,
    +            issue_priority,
    +            issue_status,
    +            issue_type,
    +            issue_severity,
    +            description=issue_obj.description,
    +            watchers=issue_obj.watchers,
    +            assigned_to=issue_obj.assigned_to,
    +        )
    +
    +        for field in issue_custom_field:
    +            is_found = False
    +            new_attr = None
    +            for attr in project_obj.list_issue_attributes():
    +                if attr.name == field["name"] and not is_found:
    +                    created.set_attribute(attr.id, field["value"])
    +                    is_found = True
    +
    +            if not is_found:
    +                new_attr = project_obj.add_issue_attribute(field["name"])
    +                created.set_attribute(new_attr.id, field["value"])
    +
    +        return created
    +
    +    def move_issue(self, issue, project):
    +        """Move issue to specific project
    +        HINT: Move will delete the issue from original circle
    +
    +        Args:
    +            issue (int , CircleIssue): issue or issue id to be movied
    +            to_project (int, Circle, FunnelCircle, ProjectCircle, TeamCircle):  circle or circle id to move issue into
    +
    +        Returns:
    +            Issue: created issue object
    +        """
    +        issue_id = issue
    +        issue_obj = issue
    +        project_id = project
    +        project_obj = project
    +        if not isinstance(issue, int):
    +            issue_id = issue.id
    +            issue_obj = issue
    +        else:
    +            issue_obj = self._client.api.issues.get(issue_id)
    +
    +        if not isinstance(project, int):
    +            project_id = project.id
    +            project_obj = project
    +        else:
    +            project_obj = Circle(self._client, self._client.api.projects.get(project_id))
    +
    +        created = self.copy_issue(issue_obj, project_obj)
    +
    +        if created:
    +            issue_obj.delete()
    +        return created
    +
    +    def create_story(self, subject="", **attrs):
    +        return self._original_object.add_user_story(subject, **attrs)
    +
    +    @property
    +    def stories(self):
    +        res = []
    +        for s in self._original_object.list_user_stories():
    +            res.append(CircleStory(self._client, s))
    +
    +        return res
    +
    +    @property
    +    def issues(self):
    +        res = []
    +        for s in self._original_object.list_issues():
    +            res.append(CircleIssue(self._client, s))
    +        return res
    +
    +    @property
    +    def circle_users(self):
    +        return self._original_object.members_objects()
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + [
    +            "create_issue",
    +            "create_story",
    +            "move_issue",
    +            "get_project_info",
    +            "as_md",
    +            "issues",
    +            "stories",
    +            "circle_users",
    +            "as_yaml",
    +        ]
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<Circle {self._original_object}>"
    +
    +    @property
    +    def as_md(self):
    +
    +        TEMPLATE = dedent(
    +            """
    +            # Circle {{project.name}}
    +
    +            - Homepage: {{project.url}}
    +            - Modified Date: {{project.modified_date}}
    +
    +            {% if project.stories %}
    +            ## Stories
    +
    +            {% for story in project.stories %}
    +
    +            ### {{story.subject}}
    +            {{story.as_md}}
    +
    +            {% endfor %}
    +
    +            {% endif %}
    +
    +
    +            {% if project.issues %}
    +            ## Issues
    +
    +            {% for issue in project.issues %}
    +
    +            ### {{issue.subject}}
    +
    +            {{issue.as_md}}
    +
    +            {% endfor %}
    +
    +            {% endif %}
    +
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, project=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        obj["basic_info"] = {
    +            "name": self.name,
    +            "slug": self.slug,
    +            "id": self.id,
    +            "description": self.description if hasattr(self, "description") else "",
    +            "tags": self.tags,
    +            "is_private": self.is_private,
    +            "looking_for_people_note": self.looking_for_people_note,
    +            "url": self.url,
    +        }
    +        obj["date"] = {
    +            "created_date": self.created_date,
    +            "modified_date": self.modified_date,
    +        }
    +        obj["modules"] = {
    +            "is_backlog_activated": self.is_backlog_activated,
    +            "is_issues_activated": self.is_issues_activated,
    +            "is_kanban_activated": self.is_kanban_activated,
    +            "is_wiki_activated": self.is_wiki_activated,
    +            "videoconferences": self.videoconferences,
    +        }
    +        owner = {
    +            "username": self.owner["username"],
    +            "id": self.owner["id"],
    +            "email": self.owner["email"],
    +        }
    +        members = [
    +            {"name": member.full_name, "id": member.id, "role": member.role_name,} for member in self.list_memberships()
    +        ]
    +        other_membership = {
    +            "i_am_owner": self.i_am_owner,
    +            "i_am_admin": self.i_am_admin,
    +            "i_am_member": self.i_am_member,
    +        }
    +        obj["membership"] = {
    +            "owner": owner,
    +            "members": members,
    +            "others": other_membership,
    +        }
    +        obj["statistics"] = {
    +            "total_activity": self.total_activity,
    +            "total_fans": self.total_fans,
    +            "total_watchers": self.total_watchers,
    +        }
    +        obj["stories"] = [story.id for story in self.stories]
    +        obj["stories_attributes"] = [st_attr.name for st_attr in self.list_user_story_attributes()]
    +        obj["issues"] = [issue.id for issue in self.issues]
    +        obj["issues_attributes"] = [issue_attr.name for issue_attr in self.list_issue_attributes()]
    +        return yaml.dump(obj)
    +
    +

    Ancestors

    + +

    Subclasses

    + +

    Instance variables

    +
    +
    var as_md
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_md(self):
    +
    +    TEMPLATE = dedent(
    +        """
    +        # Circle {{project.name}}
    +
    +        - Homepage: {{project.url}}
    +        - Modified Date: {{project.modified_date}}
    +
    +        {% if project.stories %}
    +        ## Stories
    +
    +        {% for story in project.stories %}
    +
    +        ### {{story.subject}}
    +        {{story.as_md}}
    +
    +        {% endfor %}
    +
    +        {% endif %}
    +
    +
    +        {% if project.issues %}
    +        ## Issues
    +
    +        {% for issue in project.issues %}
    +
    +        ### {{issue.subject}}
    +
    +        {{issue.as_md}}
    +
    +        {% endfor %}
    +
    +        {% endif %}
    +
    +        """
    +    )
    +    return j.tools.jinja2.render_template(template_text=TEMPLATE, project=self)
    +
    +
    +
    var as_yaml
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_yaml(self):
    +    obj = {}
    +    obj["basic_info"] = {
    +        "name": self.name,
    +        "slug": self.slug,
    +        "id": self.id,
    +        "description": self.description if hasattr(self, "description") else "",
    +        "tags": self.tags,
    +        "is_private": self.is_private,
    +        "looking_for_people_note": self.looking_for_people_note,
    +        "url": self.url,
    +    }
    +    obj["date"] = {
    +        "created_date": self.created_date,
    +        "modified_date": self.modified_date,
    +    }
    +    obj["modules"] = {
    +        "is_backlog_activated": self.is_backlog_activated,
    +        "is_issues_activated": self.is_issues_activated,
    +        "is_kanban_activated": self.is_kanban_activated,
    +        "is_wiki_activated": self.is_wiki_activated,
    +        "videoconferences": self.videoconferences,
    +    }
    +    owner = {
    +        "username": self.owner["username"],
    +        "id": self.owner["id"],
    +        "email": self.owner["email"],
    +    }
    +    members = [
    +        {"name": member.full_name, "id": member.id, "role": member.role_name,} for member in self.list_memberships()
    +    ]
    +    other_membership = {
    +        "i_am_owner": self.i_am_owner,
    +        "i_am_admin": self.i_am_admin,
    +        "i_am_member": self.i_am_member,
    +    }
    +    obj["membership"] = {
    +        "owner": owner,
    +        "members": members,
    +        "others": other_membership,
    +    }
    +    obj["statistics"] = {
    +        "total_activity": self.total_activity,
    +        "total_fans": self.total_fans,
    +        "total_watchers": self.total_watchers,
    +    }
    +    obj["stories"] = [story.id for story in self.stories]
    +    obj["stories_attributes"] = [st_attr.name for st_attr in self.list_user_story_attributes()]
    +    obj["issues"] = [issue.id for issue in self.issues]
    +    obj["issues_attributes"] = [issue_attr.name for issue_attr in self.list_issue_attributes()]
    +    return yaml.dump(obj)
    +
    +
    +
    var circle_users
    +
    +
    +
    + +Expand source code + +
    @property
    +def circle_users(self):
    +    return self._original_object.members_objects()
    +
    +
    +
    var clean_name
    +
    +
    +
    + +Expand source code + +
    @property
    +def clean_name(self):
    +    return self.slug.lower().replace("-", "_")
    +
    +
    +
    var issues
    +
    +
    +
    + +Expand source code + +
    @property
    +def issues(self):
    +    res = []
    +    for s in self._original_object.list_issues():
    +        res.append(CircleIssue(self._client, s))
    +    return res
    +
    +
    +
    var stories
    +
    +
    +
    + +Expand source code + +
    @property
    +def stories(self):
    +    res = []
    +    for s in self._original_object.list_user_stories():
    +        res.append(CircleStory(self._client, s))
    +
    +    return res
    +
    +
    +
    var url
    +
    +
    +
    + +Expand source code + +
    @property
    +def url(self):
    +    # https://circles.threefold.me/project/despiegk-tftech-software
    +    return f"{self._client.host}/project/{self.slug}"
    +
    +
    +
    +

    Methods

    +
    +
    +def copy_issue(self, issue, project) +
    +
    +

    Copy issue to specific project

    +

    Args

    +
    +
    issue : int , CircleIssue
    +
    issue or issue id to be copied
    +
    to_project : int, Circle, FunnelCircle, ProjectCircle, TeamCircle
    +
    circle or circle id to copy issue into
    +
    +

    Returns

    +
    +
    Issue
    +
    created issue object
    +
    +
    + +Expand source code + +
    def copy_issue(self, issue, project):
    +    """Copy issue to specific project
    +
    +    Args:
    +        issue (int , CircleIssue): issue or issue id to be copied
    +        to_project (int, Circle, FunnelCircle, ProjectCircle, TeamCircle):  circle or circle id to copy issue into
    +
    +    Returns:
    +        Issue: created issue object
    +    """
    +    issue_id = issue
    +    issue_obj = issue
    +    project_id = project
    +    project_obj = project
    +    if not isinstance(issue, int):
    +        issue_id = issue.id
    +        issue_obj = issue
    +    else:
    +        issue_obj = self._client.api.issues.get(issue_id)
    +
    +    if not isinstance(project, int):
    +        project_id = project.id
    +        project_obj = project
    +    else:
    +        project_obj = Circle(self._client, self._client.api.projects.get(project_id))
    +
    +    issue_priority = self.check_by_name(
    +        project_obj.priorities, self._client.api.priorities.get(issue_obj.priority).name
    +    )
    +    issue_type = self.check_by_name(
    +        project_obj.list_issue_types(), self._client.api.issue_types.get(issue_obj.type).name
    +    )
    +    issue_severity = self.check_by_name(
    +        project_obj.list_severities(), self._client.api.severities.get(issue_obj.severity).name
    +    )
    +    issue_status = self.check_by_name(project_obj.list_issue_statuses(), issue_obj.status_extra_info["name"])
    +    issue_custom_field = self._client.get_issue_custom_fields(issue_id)
    +
    +    created = project_obj.add_issue(
    +        issue_obj.subject,
    +        issue_priority,
    +        issue_status,
    +        issue_type,
    +        issue_severity,
    +        description=issue_obj.description,
    +        watchers=issue_obj.watchers,
    +        assigned_to=issue_obj.assigned_to,
    +    )
    +
    +    for field in issue_custom_field:
    +        is_found = False
    +        new_attr = None
    +        for attr in project_obj.list_issue_attributes():
    +            if attr.name == field["name"] and not is_found:
    +                created.set_attribute(attr.id, field["value"])
    +                is_found = True
    +
    +        if not is_found:
    +            new_attr = project_obj.add_issue_attribute(field["name"])
    +            created.set_attribute(new_attr.id, field["value"])
    +
    +    return created
    +
    +
    +
    +def create_issue(self, subject, prio=None, status=None, issue_type=None, severity=None) +
    +
    +
    +
    + +Expand source code + +
    def create_issue(self, subject, prio=None, status=None, issue_type=None, severity=None):
    +    prio = prio or self._original_object.list_priorities()[0].id
    +    status = status or self._original_object.list_issue_statuses()[0].id
    +    severity = severity or self._original_object.list_severities()[0].id
    +    issue_type = issue_type or self._original_object.list_issue_types()[0].id
    +
    +    return self._original_object.add_issue(subject, prio, status, issue_type, severity)
    +
    +
    +
    +def create_story(self, subject='', **attrs) +
    +
    +
    +
    + +Expand source code + +
    def create_story(self, subject="", **attrs):
    +    return self._original_object.add_user_story(subject, **attrs)
    +
    +
    +
    +def get_project_info(self) +
    +
    +
    +
    + +Expand source code + +
    @lru_cache(maxsize=128)
    +def get_project_info(self):
    +
    +    prios = list(map(lambda x: (str(x), x.id), self._original_object.list_priorities()))
    +    severities = list(map(lambda x: (str(x), x.id), self._original_object.list_severities()))
    +    statuses = list(map(lambda x: (str(x), x.id), self._original_object.list_issue_statuses()))
    +    issues_types = list(map(lambda x: (str(x), x.id), self._original_object.list_issue_types()))
    +
    +    return f"prios: {prios}, severities: {severities}, issues_types: {issues_types}, statuses: {statuses}"
    +
    +
    +
    +def move_issue(self, issue, project) +
    +
    +

    Move issue to specific project +HINT: Move will delete the issue from original circle

    +

    Args

    +
    +
    issue : int , CircleIssue
    +
    issue or issue id to be movied
    +
    to_project : int, Circle, FunnelCircle, ProjectCircle, TeamCircle
    +
    circle or circle id to move issue into
    +
    +

    Returns

    +
    +
    Issue
    +
    created issue object
    +
    +
    + +Expand source code + +
    def move_issue(self, issue, project):
    +    """Move issue to specific project
    +    HINT: Move will delete the issue from original circle
    +
    +    Args:
    +        issue (int , CircleIssue): issue or issue id to be movied
    +        to_project (int, Circle, FunnelCircle, ProjectCircle, TeamCircle):  circle or circle id to move issue into
    +
    +    Returns:
    +        Issue: created issue object
    +    """
    +    issue_id = issue
    +    issue_obj = issue
    +    project_id = project
    +    project_obj = project
    +    if not isinstance(issue, int):
    +        issue_id = issue.id
    +        issue_obj = issue
    +    else:
    +        issue_obj = self._client.api.issues.get(issue_id)
    +
    +    if not isinstance(project, int):
    +        project_id = project.id
    +        project_obj = project
    +    else:
    +        project_obj = Circle(self._client, self._client.api.projects.get(project_id))
    +
    +    created = self.copy_issue(issue_obj, project_obj)
    +
    +    if created:
    +        issue_obj.delete()
    +    return created
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class CircleIssue +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class CircleIssue(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<Issue {self._original_object}>"
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/project/despiegk-tftech-software/issue/55
    +        return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/issue/{self.ref}"
    +
    +    @property
    +    def as_md(self):
    +        TEMPLATE = dedent(
    +            """
    +            - **Subject:** [{{issue.subject}}]({{issue.url}})
    +            - **Created Date:** {{issue.created_date or 'unknown' }}
    +            - **Due Date:** {{issue.due_date or 'unknown' }}
    +            - **Owner Name:** {{issue.owner_extra_info.get('username', 'unknown')}}
    +            - **Owner Email:** {{issue.owner_extra_info.get('email', 'unknown')}}
    +            - **Project:** {{issue.project_extra_info.get('name', 'unknown')}}
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, issue=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        if self.assigned_to_extra_info is not None:
    +            obj["assigned_to"] = {
    +                "username": self.assigned_to_extra_info.get("username"),
    +                "id": self.assigned_to,
    +                "email": self.assigned_to_extra_info.get("email"),
    +            }
    +        obj["basic_info"] = {
    +            "subject": self.subject,
    +            "id": self.id,
    +            "description": self.description if hasattr(self, "description") else "",
    +            "tags": self.tags,
    +            "version": self.version,
    +            "url": self.url,
    +        }
    +        obj["project"] = {
    +            "name": self.project_extra_info.get("name"),
    +            "id": self.project,
    +        }
    +        obj["status"] = {
    +            "name": self.status_extra_info.get("name"),
    +            "id": self.status,
    +        }
    +        obj["severity"] = {
    +            "name": self._client.api.severities.get(self.severity).name,
    +            "id": self.severity,
    +        }
    +        obj["priority"] = {
    +            "name": self._client.api.priorities.get(self.priority).name,
    +            "id": self.priority,
    +        }
    +        obj["type"] = {
    +            "name": self._client.api.issue_types.get(self.type).name,
    +            "id": self.type,
    +        }
    +        obj["date"] = {
    +            "created_date": self.created_date,
    +            "modified_date": self.modified_date,
    +            "due_date": self.due_date,
    +            "due_date_reason": self.due_date_reason,
    +            "due_date_status": self.due_date_status,
    +            "finished_date": self.finished_date,
    +        }
    +        owner = {
    +            "username": self.owner_extra_info["username"],
    +            "id": self.owner,
    +            "email": self.owner_extra_info["email"],
    +        }
    +        watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +        watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +        obj["membership"] = {"owner": owner, "watchers": watchers}
    +
    +        obj["statistics"] = {
    +            "total_voters": self.total_voters,
    +            "total_watchers": self.total_watchers,
    +        }
    +        obj["custom_fields"] = self._client.get_issue_custom_fields(self.id)
    +        obj["additional_info"] = {
    +            "is_blocked": self.is_blocked,
    +            "is_closed": self.is_closed,
    +            "is_voter": self.is_voter,
    +            "is_watcher": self.is_watcher,
    +            "blocked_note": self.blocked_note,
    +        }
    +        return yaml.dump(obj)
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + ["as_yaml", "url", "as_md"]
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var as_md
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_md(self):
    +    TEMPLATE = dedent(
    +        """
    +        - **Subject:** [{{issue.subject}}]({{issue.url}})
    +        - **Created Date:** {{issue.created_date or 'unknown' }}
    +        - **Due Date:** {{issue.due_date or 'unknown' }}
    +        - **Owner Name:** {{issue.owner_extra_info.get('username', 'unknown')}}
    +        - **Owner Email:** {{issue.owner_extra_info.get('email', 'unknown')}}
    +        - **Project:** {{issue.project_extra_info.get('name', 'unknown')}}
    +        """
    +    )
    +    return j.tools.jinja2.render_template(template_text=TEMPLATE, issue=self)
    +
    +
    +
    var as_yaml
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_yaml(self):
    +    obj = {}
    +    if self.assigned_to_extra_info is not None:
    +        obj["assigned_to"] = {
    +            "username": self.assigned_to_extra_info.get("username"),
    +            "id": self.assigned_to,
    +            "email": self.assigned_to_extra_info.get("email"),
    +        }
    +    obj["basic_info"] = {
    +        "subject": self.subject,
    +        "id": self.id,
    +        "description": self.description if hasattr(self, "description") else "",
    +        "tags": self.tags,
    +        "version": self.version,
    +        "url": self.url,
    +    }
    +    obj["project"] = {
    +        "name": self.project_extra_info.get("name"),
    +        "id": self.project,
    +    }
    +    obj["status"] = {
    +        "name": self.status_extra_info.get("name"),
    +        "id": self.status,
    +    }
    +    obj["severity"] = {
    +        "name": self._client.api.severities.get(self.severity).name,
    +        "id": self.severity,
    +    }
    +    obj["priority"] = {
    +        "name": self._client.api.priorities.get(self.priority).name,
    +        "id": self.priority,
    +    }
    +    obj["type"] = {
    +        "name": self._client.api.issue_types.get(self.type).name,
    +        "id": self.type,
    +    }
    +    obj["date"] = {
    +        "created_date": self.created_date,
    +        "modified_date": self.modified_date,
    +        "due_date": self.due_date,
    +        "due_date_reason": self.due_date_reason,
    +        "due_date_status": self.due_date_status,
    +        "finished_date": self.finished_date,
    +    }
    +    owner = {
    +        "username": self.owner_extra_info["username"],
    +        "id": self.owner,
    +        "email": self.owner_extra_info["email"],
    +    }
    +    watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +    watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +    obj["membership"] = {"owner": owner, "watchers": watchers}
    +
    +    obj["statistics"] = {
    +        "total_voters": self.total_voters,
    +        "total_watchers": self.total_watchers,
    +    }
    +    obj["custom_fields"] = self._client.get_issue_custom_fields(self.id)
    +    obj["additional_info"] = {
    +        "is_blocked": self.is_blocked,
    +        "is_closed": self.is_closed,
    +        "is_voter": self.is_voter,
    +        "is_watcher": self.is_watcher,
    +        "blocked_note": self.blocked_note,
    +    }
    +    return yaml.dump(obj)
    +
    +
    +
    var url
    +
    +
    +
    + +Expand source code + +
    @property
    +def url(self):
    +    # https://circles.threefold.me/project/despiegk-tftech-software/issue/55
    +    return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/issue/{self.ref}"
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class CircleResource +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class CircleResource:
    +    def __init__(self, taigaclient, original_object):
    +        self._original_object = original_object
    +        self._client = taigaclient
    +        self._api = self._client.api
    +
    +    def check_by_name(self, list_obj, name_to_find):
    +        """Check if list contains name, used to check priorities, severities, statuses, types and etc
    +            and return the equivalent id.
    +
    +        Args:
    +            list_obj (list): list of objects that we need to search into
    +            name_to_find (str): word to search for in the list
    +
    +        Returns:
    +            int: Id of the matched object if found, or id of the first object in the list.
    +        """
    +        for obj in list_obj:
    +            if obj.name == name_to_find:
    +                return obj.id
    +
    +        return list_obj[0].id
    +
    +

    Subclasses

    + +

    Methods

    +
    +
    +def check_by_name(self, list_obj, name_to_find) +
    +
    +

    Check if list contains name, used to check priorities, severities, statuses, types and etc +and return the equivalent id.

    +

    Args

    +
    +
    list_obj : list
    +
    list of objects that we need to search into
    +
    name_to_find : str
    +
    word to search for in the list
    +
    +

    Returns

    +
    +
    int
    +
    Id of the matched object if found, or id of the first object in the list.
    +
    +
    + +Expand source code + +
    def check_by_name(self, list_obj, name_to_find):
    +    """Check if list contains name, used to check priorities, severities, statuses, types and etc
    +        and return the equivalent id.
    +
    +    Args:
    +        list_obj (list): list of objects that we need to search into
    +        name_to_find (str): word to search for in the list
    +
    +    Returns:
    +        int: Id of the matched object if found, or id of the first object in the list.
    +    """
    +    for obj in list_obj:
    +        if obj.name == name_to_find:
    +            return obj.id
    +
    +    return list_obj[0].id
    +
    +
    +
    +
    +
    +class CircleStory +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class CircleStory(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<Story {self._original_object}>"
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/project/despiegk-tftech-software/us/4
    +        return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/us/{self.ref}"
    +
    +    @property
    +    def circle_tasks(self):
    +        return [CircleTask(self._client, task) for task in self.list_tasks()]
    +
    +    @property
    +    def as_md(self):
    +        TEMPLATE = dedent(
    +            """
    +            - **Subject:** [{{story.subject}}]({{story.url}})
    +            - **Assigned to:** {{story.assigned_to_extra_info and story.assigned_to_extra_info.get('username', 'not assigned') or 'not assigned' }}
    +            - **Watchers:** {{story.watchers or 'no watchers'}}
    +            {% if story.tasks %}
    +            - **Tasks**:
    +                {% for task in story.circle_tasks %}
    +                - [**{{task.subject}}**]({{task.url}})
    +                {% endfor %}
    +            {% endif %}
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, story=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        if self.assigned_to_extra_info is not None:
    +            obj["assigned_to"] = {
    +                "username": self.assigned_to_extra_info.get("username") or None,
    +                "id": self.assigned_to,
    +                "email": self.assigned_to_extra_info.get("email"),
    +            }
    +
    +        obj["basic_info"] = {
    +            "subject": self.subject,
    +            "id": self.id,
    +            "description": self.description if hasattr(self, "description") else "",
    +            "tags": self.tags,
    +            "version": self.version,
    +            "url": self.url,
    +        }
    +        obj["project"] = {
    +            "name": self.project_extra_info.get("name"),
    +            "id": self.project,
    +        }
    +        obj["status"] = {
    +            "name": self.status_extra_info.get("name"),
    +            "id": self.status,
    +        }
    +
    +        obj["date"] = {
    +            "created_date": self.created_date,
    +            "modified_date": self.modified_date,
    +            "due_date": self.due_date,
    +            "due_date_reason": self.due_date_reason,
    +            "due_date_status": self.due_date_status,
    +            "finish_date": self.finish_date,
    +        }
    +        owner = {
    +            "username": self.owner_extra_info["username"],
    +            "id": self.owner,
    +            "email": self.owner_extra_info["email"],
    +        }
    +        watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +        watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +        obj["membership"] = {"owner": owner, "watchers": watchers}
    +        obj["requirements"] = {
    +            "client_requirement": self.client_requirement,
    +            "team_requirement": self.team_requirement,
    +        }
    +        obj["statistics"] = {
    +            "total_attachments": self.total_attachments,
    +            "total_comments": self.total_comments,
    +            "total_voters": self.total_voters,
    +            "total_watchers": self.total_watchers,
    +        }
    +        obj["custom_fields"] = self._client.get_story_custom_fields(self.id)
    +        obj["tasks"] = [task.id for task in self.tasks]
    +        obj["additional_info"] = {
    +            "is_blocked": self.is_blocked,
    +            "is_closed": self.is_closed,
    +            "is_voter": self.is_voter,
    +            "is_watcher": self.is_watcher,
    +            "blocked_note": self.blocked_note,
    +            "generated_from_issue": self.generated_from_issue,
    +            "generated_from_task": self.generated_from_task,
    +        }
    +        return yaml.dump(obj)
    +
    +    @property
    +    def tasks(self):
    +        return self.list_tasks()
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + ["as_yaml", "circle_tasks", "url", "as_md"]
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var as_md
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_md(self):
    +    TEMPLATE = dedent(
    +        """
    +        - **Subject:** [{{story.subject}}]({{story.url}})
    +        - **Assigned to:** {{story.assigned_to_extra_info and story.assigned_to_extra_info.get('username', 'not assigned') or 'not assigned' }}
    +        - **Watchers:** {{story.watchers or 'no watchers'}}
    +        {% if story.tasks %}
    +        - **Tasks**:
    +            {% for task in story.circle_tasks %}
    +            - [**{{task.subject}}**]({{task.url}})
    +            {% endfor %}
    +        {% endif %}
    +        """
    +    )
    +    return j.tools.jinja2.render_template(template_text=TEMPLATE, story=self)
    +
    +
    +
    var as_yaml
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_yaml(self):
    +    obj = {}
    +    if self.assigned_to_extra_info is not None:
    +        obj["assigned_to"] = {
    +            "username": self.assigned_to_extra_info.get("username") or None,
    +            "id": self.assigned_to,
    +            "email": self.assigned_to_extra_info.get("email"),
    +        }
    +
    +    obj["basic_info"] = {
    +        "subject": self.subject,
    +        "id": self.id,
    +        "description": self.description if hasattr(self, "description") else "",
    +        "tags": self.tags,
    +        "version": self.version,
    +        "url": self.url,
    +    }
    +    obj["project"] = {
    +        "name": self.project_extra_info.get("name"),
    +        "id": self.project,
    +    }
    +    obj["status"] = {
    +        "name": self.status_extra_info.get("name"),
    +        "id": self.status,
    +    }
    +
    +    obj["date"] = {
    +        "created_date": self.created_date,
    +        "modified_date": self.modified_date,
    +        "due_date": self.due_date,
    +        "due_date_reason": self.due_date_reason,
    +        "due_date_status": self.due_date_status,
    +        "finish_date": self.finish_date,
    +    }
    +    owner = {
    +        "username": self.owner_extra_info["username"],
    +        "id": self.owner,
    +        "email": self.owner_extra_info["email"],
    +    }
    +    watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +    watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +    obj["membership"] = {"owner": owner, "watchers": watchers}
    +    obj["requirements"] = {
    +        "client_requirement": self.client_requirement,
    +        "team_requirement": self.team_requirement,
    +    }
    +    obj["statistics"] = {
    +        "total_attachments": self.total_attachments,
    +        "total_comments": self.total_comments,
    +        "total_voters": self.total_voters,
    +        "total_watchers": self.total_watchers,
    +    }
    +    obj["custom_fields"] = self._client.get_story_custom_fields(self.id)
    +    obj["tasks"] = [task.id for task in self.tasks]
    +    obj["additional_info"] = {
    +        "is_blocked": self.is_blocked,
    +        "is_closed": self.is_closed,
    +        "is_voter": self.is_voter,
    +        "is_watcher": self.is_watcher,
    +        "blocked_note": self.blocked_note,
    +        "generated_from_issue": self.generated_from_issue,
    +        "generated_from_task": self.generated_from_task,
    +    }
    +    return yaml.dump(obj)
    +
    +
    +
    var circle_tasks
    +
    +
    +
    + +Expand source code + +
    @property
    +def circle_tasks(self):
    +    return [CircleTask(self._client, task) for task in self.list_tasks()]
    +
    +
    +
    var tasks
    +
    +
    +
    + +Expand source code + +
    @property
    +def tasks(self):
    +    return self.list_tasks()
    +
    +
    +
    var url
    +
    +
    +
    + +Expand source code + +
    @property
    +def url(self):
    +    # https://circles.threefold.me/project/despiegk-tftech-software/us/4
    +    return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/us/{self.ref}"
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class CircleTask +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class CircleTask(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<Task {self._original_object}>"
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/project/despiegk-tftech-software/task/2
    +        return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/task/{self.ref}"
    +
    +    @property
    +    def as_md(self):
    +        TEMPLATE = dedent(
    +            """
    +            - **Subject:** [{{task.subject}}]({{task.url}})
    +            - **Created Date:** {{task.created_date or 'unknown' }}
    +            - **Due Date:** {{task.due_date or 'unknown' }}
    +            - **Owner Name:** {{task.owner_extra_info.get('username', 'unknown')}}
    +            - **Owner Email:** {{task.owner_extra_info.get('email', 'unknown')}}
    +            - **Project:** {{task.project_extra_info.get('name', 'unknown')}}
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, task=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        if self.assigned_to_extra_info is not None:
    +            obj["assigned_to"] = {
    +                "username": self.assigned_to_extra_info.get("username") or None,
    +                "id": self.assigned_to,
    +                "email": self.assigned_to_extra_info.get("email"),
    +            }
    +
    +        obj["basic_info"] = {
    +            "subject": self.subject,
    +            "id": self.id,
    +            "description": self.description if hasattr(self, "description") else "",
    +            "tags": self.tags,
    +            "version": self.version,
    +            "url": self.url,
    +        }
    +        obj["project"] = {
    +            "name": self.project_extra_info.get("name"),
    +            "id": self.project,
    +        }
    +        obj["status"] = {
    +            "name": self.status_extra_info.get("name"),
    +            "id": self.status,
    +        }
    +        obj["user_story"] = {
    +            "subject": self.user_story_extra_info.get("subject"),
    +            "id": self.user_story,
    +        }
    +        obj["date"] = {
    +            "created_date": self.created_date,
    +            "modified_date": self.modified_date,
    +            "due_date": self.due_date,
    +            "due_date_reason": self.due_date_reason,
    +            "due_date_status": self.due_date_status,
    +            "finished_date": self.finished_date,
    +        }
    +        owner = {
    +            "username": self.owner_extra_info["username"],
    +            "id": self.owner,
    +            "email": self.owner_extra_info["email"],
    +        }
    +        watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +        watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +        obj["membership"] = {"owner": owner, "watchers": watchers}
    +        obj["statistics"] = {
    +            "total_comments": self.total_comments,
    +            "total_voters": self.total_voters,
    +            "total_watchers": self.total_watchers,
    +        }
    +        obj["additional_info"] = {
    +            "is_blocked": self.is_blocked,
    +            "is_closed": self.is_closed,
    +            "is_voter": self.is_voter,
    +            "is_watcher": self.is_watcher,
    +            "blocked_note": self.blocked_note,
    +        }
    +        return yaml.dump(obj)
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + ["as_yaml", "url", "as_md"]
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var as_md
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_md(self):
    +    TEMPLATE = dedent(
    +        """
    +        - **Subject:** [{{task.subject}}]({{task.url}})
    +        - **Created Date:** {{task.created_date or 'unknown' }}
    +        - **Due Date:** {{task.due_date or 'unknown' }}
    +        - **Owner Name:** {{task.owner_extra_info.get('username', 'unknown')}}
    +        - **Owner Email:** {{task.owner_extra_info.get('email', 'unknown')}}
    +        - **Project:** {{task.project_extra_info.get('name', 'unknown')}}
    +        """
    +    )
    +    return j.tools.jinja2.render_template(template_text=TEMPLATE, task=self)
    +
    +
    +
    var as_yaml
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_yaml(self):
    +    obj = {}
    +    if self.assigned_to_extra_info is not None:
    +        obj["assigned_to"] = {
    +            "username": self.assigned_to_extra_info.get("username") or None,
    +            "id": self.assigned_to,
    +            "email": self.assigned_to_extra_info.get("email"),
    +        }
    +
    +    obj["basic_info"] = {
    +        "subject": self.subject,
    +        "id": self.id,
    +        "description": self.description if hasattr(self, "description") else "",
    +        "tags": self.tags,
    +        "version": self.version,
    +        "url": self.url,
    +    }
    +    obj["project"] = {
    +        "name": self.project_extra_info.get("name"),
    +        "id": self.project,
    +    }
    +    obj["status"] = {
    +        "name": self.status_extra_info.get("name"),
    +        "id": self.status,
    +    }
    +    obj["user_story"] = {
    +        "subject": self.user_story_extra_info.get("subject"),
    +        "id": self.user_story,
    +    }
    +    obj["date"] = {
    +        "created_date": self.created_date,
    +        "modified_date": self.modified_date,
    +        "due_date": self.due_date,
    +        "due_date_reason": self.due_date_reason,
    +        "due_date_status": self.due_date_status,
    +        "finished_date": self.finished_date,
    +    }
    +    owner = {
    +        "username": self.owner_extra_info["username"],
    +        "id": self.owner,
    +        "email": self.owner_extra_info["email"],
    +    }
    +    watchers_objects = [self._client.api.users.get(id) for id in self.watchers]
    +    watchers = [f"({watcher.id}) {watcher.username}" for watcher in watchers_objects]
    +    obj["membership"] = {"owner": owner, "watchers": watchers}
    +    obj["statistics"] = {
    +        "total_comments": self.total_comments,
    +        "total_voters": self.total_voters,
    +        "total_watchers": self.total_watchers,
    +    }
    +    obj["additional_info"] = {
    +        "is_blocked": self.is_blocked,
    +        "is_closed": self.is_closed,
    +        "is_voter": self.is_voter,
    +        "is_watcher": self.is_watcher,
    +        "blocked_note": self.blocked_note,
    +    }
    +    return yaml.dump(obj)
    +
    +
    +
    var url
    +
    +
    +
    + +Expand source code + +
    @property
    +def url(self):
    +    # https://circles.threefold.me/project/despiegk-tftech-software/task/2
    +    return f"{self._client.host}/project/{self.project_extra_info.get('slug')}/task/{self.ref}"
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class CircleUser +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class CircleUser(CircleResource):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        return getattr(self._original_object, attr)
    +
    +    def __str__(self):
    +        return f"<User {self._original_object}>"
    +
    +    @property
    +    def url(self):
    +        # https://circles.threefold.me/profile/ahartl
    +        return f"{self._client.host}/profile/{self.username}"
    +
    +    @property
    +    def clean_name(self):
    +        return self.username.lower()
    +
    +    def get_stories(self):
    +        return self._client.list_all_user_stories(self._original_object.username)
    +
    +    def get_issues(self):
    +        return self._client.list_all_issues(self._original_object.username)
    +
    +    def get_circles(self):
    +        circles = []
    +        all_circles = self._client.get_user_circles(self._original_object.username)
    +        for c in all_circles:
    +            cname = c.name.lower()
    +            if cname.startswith("team"):
    +                circles.append(TeamCircle(self._client, c))
    +            elif cname.startswith("funnel"):
    +                circles.append(FunnelCircle(self._client, c))
    +            elif cname.startswith("project"):
    +                circles.append(ProjectCircle(self._client, c))
    +            elif cname.startswith("archive"):
    +                pass  # to not included in as_md property
    +            else:
    +                circles.append(Circle(self._client, c))
    +        return circles
    +
    +    def get_tasks(self):
    +        return self._client.list_all_tasks(self._original_object.username)
    +
    +    @property
    +    def stories(self):
    +        res = []
    +        for s in self.get_stories():
    +            res.append(CircleStory(self._client, s))
    +
    +        return res
    +
    +    @property
    +    def issues(self):
    +        res = []
    +        for s in self.get_issues():
    +            res.append(CircleIssue(self._client, s))
    +
    +        return res
    +
    +    @property
    +    def circles(self):
    +        return self.get_circles()
    +
    +    @property
    +    def tasks(self):
    +        return self.get_tasks()
    +
    +    @property
    +    def as_md(self):
    +
    +        TEMPLATE = dedent(
    +            """
    +            # {{user.username}}
    +
    +            User profile is at: [{{user.url}}]({{user.url}})
    +
    +            {% if user.circles %}
    +            ## Circles
    +
    +            {% for c in user.circles %}
    +
    +            '[{{c.name}}]({{c.clean_name}}.md) [{{c.url}}]({{c.url}})
    +            {% endfor %}
    +
    +            {% endif %}
    +
    +            {% if user.stories %}
    +            ## User stories
    +            {% for story in user.stories %}
    +
    +            ### {{story.subject}}
    +
    +            {{story.as_md}}
    +
    +            {% endfor %}
    +
    +            {% endif %}
    +
    +
    +            {% if user.issues %}
    +            ## Issues
    +
    +            {% for issue in user.issues %}
    +            ### {{issue.subject}}
    +
    +            {{issue.as_md}}
    +
    +            {% endfor %}
    +            {% endif %}
    +
    +            {% if user.tasks %}
    +            ## Tasks
    +
    +            {% for task in user.tasks %}
    +            ### {{task.subject}}
    +
    +            {{task.as_md}}
    +
    +            {% endfor %}
    +            {% endif %}
    +
    +            """
    +        )
    +        return j.tools.jinja2.render_template(template_text=TEMPLATE, user=self)
    +
    +    @property
    +    def as_yaml(self):
    +        obj = {}
    +        obj["basic_info"] = {
    +            "username": self.username,
    +            "email": self.email,
    +            "id": self.id,
    +            "full_name": self.full_name,
    +            "bio": self.bio,
    +            "lang": self.lang,
    +            "public_key": self.public_key,
    +            "photo": self.photo,
    +            "url": self.url,
    +        }
    +        obj["date"] = {"date_joined": self.date_joined, "timezone": self.timezone}
    +        obj["statistics"] = {
    +            "total_private_projects": self.total_private_projects,
    +            "total_public_projects": self.total_public_projects,
    +        }
    +        obj["roles"] = self.roles
    +        obj["limits"] = {
    +            "max_memberships_private_projects": self.max_memberships_private_projects,
    +            "max_memberships_public_projects": self.max_memberships_public_projects,
    +            "max_private_projects": self.max_private_projects,
    +            "max_public_projects": self.max_memberships_public_projects,
    +        }
    +        obj["terms"] = {
    +            "accepted_terms": self.accepted_terms,
    +            "read_new_terms": self.read_new_terms,
    +        }
    +        return yaml.dump(obj)
    +
    +    def __dir__(self):
    +        return dir(self._original_object) + [
    +            "as_yaml",
    +            "as_md",
    +            "issues",
    +            "stories",
    +            "tasks",
    +            "get_circles",
    +            "get_issues",
    +            "get_stories",
    +            "get_tasks",
    +            "url",
    +            "clean_name",
    +        ]
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var as_md
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_md(self):
    +
    +    TEMPLATE = dedent(
    +        """
    +        # {{user.username}}
    +
    +        User profile is at: [{{user.url}}]({{user.url}})
    +
    +        {% if user.circles %}
    +        ## Circles
    +
    +        {% for c in user.circles %}
    +
    +        '[{{c.name}}]({{c.clean_name}}.md) [{{c.url}}]({{c.url}})
    +        {% endfor %}
    +
    +        {% endif %}
    +
    +        {% if user.stories %}
    +        ## User stories
    +        {% for story in user.stories %}
    +
    +        ### {{story.subject}}
    +
    +        {{story.as_md}}
    +
    +        {% endfor %}
    +
    +        {% endif %}
    +
    +
    +        {% if user.issues %}
    +        ## Issues
    +
    +        {% for issue in user.issues %}
    +        ### {{issue.subject}}
    +
    +        {{issue.as_md}}
    +
    +        {% endfor %}
    +        {% endif %}
    +
    +        {% if user.tasks %}
    +        ## Tasks
    +
    +        {% for task in user.tasks %}
    +        ### {{task.subject}}
    +
    +        {{task.as_md}}
    +
    +        {% endfor %}
    +        {% endif %}
    +
    +        """
    +    )
    +    return j.tools.jinja2.render_template(template_text=TEMPLATE, user=self)
    +
    +
    +
    var as_yaml
    +
    +
    +
    + +Expand source code + +
    @property
    +def as_yaml(self):
    +    obj = {}
    +    obj["basic_info"] = {
    +        "username": self.username,
    +        "email": self.email,
    +        "id": self.id,
    +        "full_name": self.full_name,
    +        "bio": self.bio,
    +        "lang": self.lang,
    +        "public_key": self.public_key,
    +        "photo": self.photo,
    +        "url": self.url,
    +    }
    +    obj["date"] = {"date_joined": self.date_joined, "timezone": self.timezone}
    +    obj["statistics"] = {
    +        "total_private_projects": self.total_private_projects,
    +        "total_public_projects": self.total_public_projects,
    +    }
    +    obj["roles"] = self.roles
    +    obj["limits"] = {
    +        "max_memberships_private_projects": self.max_memberships_private_projects,
    +        "max_memberships_public_projects": self.max_memberships_public_projects,
    +        "max_private_projects": self.max_private_projects,
    +        "max_public_projects": self.max_memberships_public_projects,
    +    }
    +    obj["terms"] = {
    +        "accepted_terms": self.accepted_terms,
    +        "read_new_terms": self.read_new_terms,
    +    }
    +    return yaml.dump(obj)
    +
    +
    +
    var circles
    +
    +
    +
    + +Expand source code + +
    @property
    +def circles(self):
    +    return self.get_circles()
    +
    +
    +
    var clean_name
    +
    +
    +
    + +Expand source code + +
    @property
    +def clean_name(self):
    +    return self.username.lower()
    +
    +
    +
    var issues
    +
    +
    +
    + +Expand source code + +
    @property
    +def issues(self):
    +    res = []
    +    for s in self.get_issues():
    +        res.append(CircleIssue(self._client, s))
    +
    +    return res
    +
    +
    +
    var stories
    +
    +
    +
    + +Expand source code + +
    @property
    +def stories(self):
    +    res = []
    +    for s in self.get_stories():
    +        res.append(CircleStory(self._client, s))
    +
    +    return res
    +
    +
    +
    var tasks
    +
    +
    +
    + +Expand source code + +
    @property
    +def tasks(self):
    +    return self.get_tasks()
    +
    +
    +
    var url
    +
    +
    +
    + +Expand source code + +
    @property
    +def url(self):
    +    # https://circles.threefold.me/profile/ahartl
    +    return f"{self._client.host}/profile/{self.username}"
    +
    +
    +
    +

    Methods

    +
    +
    +def get_circles(self) +
    +
    +
    +
    + +Expand source code + +
    def get_circles(self):
    +    circles = []
    +    all_circles = self._client.get_user_circles(self._original_object.username)
    +    for c in all_circles:
    +        cname = c.name.lower()
    +        if cname.startswith("team"):
    +            circles.append(TeamCircle(self._client, c))
    +        elif cname.startswith("funnel"):
    +            circles.append(FunnelCircle(self._client, c))
    +        elif cname.startswith("project"):
    +            circles.append(ProjectCircle(self._client, c))
    +        elif cname.startswith("archive"):
    +            pass  # to not included in as_md property
    +        else:
    +            circles.append(Circle(self._client, c))
    +    return circles
    +
    +
    +
    +def get_issues(self) +
    +
    +
    +
    + +Expand source code + +
    def get_issues(self):
    +    return self._client.list_all_issues(self._original_object.username)
    +
    +
    +
    +def get_stories(self) +
    +
    +
    +
    + +Expand source code + +
    def get_stories(self):
    +    return self._client.list_all_user_stories(self._original_object.username)
    +
    +
    +
    +def get_tasks(self) +
    +
    +
    +
    + +Expand source code + +
    def get_tasks(self):
    +    return self._client.list_all_tasks(self._original_object.username)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class FunnelCircle +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class FunnelCircle(Circle):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        try:
    +            return getattr(self._original_object, attr)
    +        except Exception as e:
    +            if hasattr(self._original_object, "_original_object"):
    +                return getattr(self._original_object._original_object, attr)
    +            else:
    +                raise j.exceptions.Runtime(f"{attr} not found in {self} and not in {self._original_object}")
    +
    +    def __str__(self):
    +        return f"<FunnelCircle {self._original_object}>"
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +class ProjectCircle +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class ProjectCircle(Circle):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        try:
    +            return getattr(self._original_object, attr)
    +        except Exception as e:
    +            if hasattr(self._original_object, "_original_object"):
    +                return getattr(self._original_object._original_object, attr)
    +            else:
    +                raise j.exceptions.Runtime(f"{attr} not found in {self} and not in {self._original_object}")
    +
    +    def __str__(self):
    +        return f"<ProjectCircle {self._original_object}>"
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +class TeamCircle +(taigaclient, original_object) +
    +
    +
    +
    + +Expand source code + +
    class TeamCircle(Circle):
    +    def __init__(self, taigaclient, original_object):
    +        super().__init__(taigaclient, original_object)
    +
    +    def __getattr__(self, attr):
    +        try:
    +            return getattr(self._original_object, attr)
    +        except Exception as e:
    +            if hasattr(self._original_object, "_original_object"):
    +                return getattr(self._original_object._original_object, attr)
    +            else:
    +                raise j.exceptions.Runtime(f"{attr} not found in {self} and not in {self._original_object}")
    +
    +    def __str__(self):
    +        return f"<TeamCircle {self._original_object}>"
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/taiga/taiga.html b/docs/api/jumpscale/clients/taiga/taiga.html new file mode 100644 index 000000000..868e9ec1f --- /dev/null +++ b/docs/api/jumpscale/clients/taiga/taiga.html @@ -0,0 +1,4499 @@ + + + + + + +jumpscale.clients.taiga.taiga API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.taiga.taiga

    +
    +
    +

    Taiga client

    +

    Initialization

    +

    Using username and +password:

    +
    client = j.clients.taiga.new('test', host="https://staging.circles.threefold.me/", username='admin', password='123456')
    +
    +

    OR using a token

    +
    client = j.clients.taiga.new('test', host="https://staging.circles.threefold.me/", token='extra secret token string')
    +
    +

    Listing

    +

    Listing issues

    +

    To get the issues of the user with id 123:

    +
    client.list_all_issues(123)
    +
    +

    To get the issues of all users:

    +
    client.list_all_issues()
    +
    +
    +

    Listing projects

    +

    To list all projects:

    +
    client.list_all_projects()
    +
    +

    To list all projects not start with ARCHIVE:

    +
    client.list_all_active_projects()
    +
    +
    +

    Listing user stories

    +

    To list the user stories of the user with id 123:

    +
    client.list_all_user_stories(123)
    +
    +

    To list the user stories of all users:

    +
    client.list_all_user_stories()
    +
    +

    Listing tasks

    +

    To list the tasks of the user with id 123:

    +
    client.list_all_tasks(123)
    +
    +

    To list the tasks of all users :

    +
    client.list_all_tasks()
    +
    +

    List team circles

    +
    client.list_team_circles()
    +
    +

    List project circles

    +
    client.list_project_circles()
    +
    +

    List funnel circles

    +
    client.list_funnel_circles()
    +
    +
    +

    Custom Fields

    +

    Get

    +

    To get issue custom fields for the issue with id 123

    +
    custom_fields = client.get_issue_custom_fields(123)
    +
    +

    To get user story custom fields for the story with id 123

    +
    custom_fields = client.get_story_custom_fields(123)
    +
    +

    Validate

    +

    To validate custom field according to specs

    +
    client.validate_custom_fields(custom_fields)
    +
    +

    Creating

    +

    Create new circle

    +

    if you want full control on the circle creation on priorities, severities, .. etc, you can use _create_new_circle method

    +
    def _create_new_circle(
    +    self,
    +    name,
    +    type_="team",
    +    description="desc",
    +    severities=None,
    +    issues_statuses=None,
    +    priorities=None,
    +    issues_types=None,
    +    user_stories_statuses=None,
    +    tasks_statuses=None,
    +    custom_fields=None
    +    **attrs,
    +):
    +
    +

    otherwise you can use create_new_project_circle,, create_new_team_circle, create_new_funnel_circle

    +

    Create new story

    +
    circle_object.create_story("abc")
    +
    +

    Create a new +issue

    +
    create_issue("my issue")
    +
    +

    Exporting

    +

    Export users and circles

    +
    client.export_as_md("/tmp/taigawiki")
    +
    +

    Export users

    +
    client.export_users_as_md("/tmp/taigawiki")
    +
    +

    Export circles

    +
    client.export_circles_as_md("/tmp/taigawiki")
    +
    +

    Export users and circles periodically

    +

    To export users and circles periodically each 10 minutes

    +
    client.export_as_md_periodically("/tmp/taigawiki", period= 600)
    +
    +
    +

    period use seconds as time unit.

    +
    +

    Export objects as yaml

    +

    To export All objects as yaml all you need is

    +
    client.export_as_yaml("/tmp/exported_taiga_dir")
    +
    +

    This will export resources (users, projects, issues, stories, tasks) in `/tmp/exported_taiga_dir/$object_type/$object_id.yaml

    +

    Importing

    +

    Importing from yaml files

    +

    To import from yaml files files which exported using export_as_yaml

    +
    client.import_from_yaml("/tmp/exported_taiga_dir")
    +
    +

    This will import resources (projects, issues, stories, tasks) as a new instance import basic info till now

    +

    Operations

    +

    Move a story to a project

    +
    client.move_story_to_circle(789, 123) # story id, project id
    +
    +

    Copy and Move Issue using project object

    +
    project_object.copy_issue(issue_id_or_issue_object, project_id_or_project_object)
    +project_object.move_issue(issue_id_or_issue_object, project_id_or_project_object)
    +
    +
    +

    Keep in mind that move will delete the issue from the original project

    +
    +

    Resources urls

    +

    All of resources e.g (user, issue, user_story, circle, task) have url, as_md and as_yaml properties

    +
    + +Expand source code + +
    """
    +# Taiga client
    +
    +## Initialization
    +
    +Using username and  password:
    +```
    +client = j.clients.taiga.new('test', host="https://staging.circles.threefold.me/", username='admin', password='123456')
    +```
    +OR using a token
    +
    +```
    +client = j.clients.taiga.new('test', host="https://staging.circles.threefold.me/", token='extra secret token string')
    +```
    +## Listing
    +
    +### Listing issues
    +
    +To get the issues of the user with id 123:
    +```
    +client.list_all_issues(123)
    +```
    +To get the issues of all users:
    +```
    +client.list_all_issues()
    +
    +```
    +### Listing projects
    +
    +To list all projects:
    +```
    +client.list_all_projects()
    +```
    +
    +To list all projects not start with **ARCHIVE**:
    +```
    +client.list_all_active_projects()
    +
    +```
    +### Listing user stories
    +
    +To list the user stories of the user with id 123:
    +
    +```
    +client.list_all_user_stories(123)
    +```
    +
    +To list the user stories of all users:
    +
    +```
    +client.list_all_user_stories()
    +```
    +
    +### Listing tasks
    +
    +To list the tasks of the user with id 123:
    +
    +```
    +client.list_all_tasks(123)
    +```
    +
    +To list the tasks of all users :
    +
    +```
    +client.list_all_tasks()
    +```
    +
    +
    +### List team circles
    +
    +```
    +client.list_team_circles()
    +```
    +
    +
    +### List project circles
    +
    +```
    +client.list_project_circles()
    +```
    +
    +### List funnel circles
    +
    +```
    +client.list_funnel_circles()
    +
    +```
    +
    +## Custom Fields
    +
    +### Get
    +
    +To get issue custom fields for the issue with id 123
    +
    +```
    +custom_fields = client.get_issue_custom_fields(123)
    +```
    +
    +To get user story custom fields for the story with id 123
    +
    +```
    +custom_fields = client.get_story_custom_fields(123)
    +```
    +
    +### Validate
    +
    +To validate custom field according to [specs](https://github.com/threefoldtech/circles_reporting_tool/blob/master/specs/funnel.md#custom-fields)
    +
    +```
    +client.validate_custom_fields(custom_fields)
    +```
    +
    +## Creating
    +
    +### Create new circle
    +
    +if you want full control on the circle creation on priorities, severities, .. etc, you can use `_create_new_circle` method
    +
    +
    +```
    +def _create_new_circle(
    +    self,
    +    name,
    +    type_="team",
    +    description="desc",
    +    severities=None,
    +    issues_statuses=None,
    +    priorities=None,
    +    issues_types=None,
    +    user_stories_statuses=None,
    +    tasks_statuses=None,
    +    custom_fields=None
    +    **attrs,
    +):
    +```
    +otherwise you can use `create_new_project_circle,`, `create_new_team_circle`, `create_new_funnel_circle`
    +
    +
    +### Create new story
    +
    +```
    +circle_object.create_story("abc")
    +```
    +
    +### Create a new  issue
    +
    +```
    +create_issue("my issue")
    +```
    +
    +## Exporting
    +
    +### Export users and circles
    +
    +```
    +client.export_as_md("/tmp/taigawiki")
    +```
    +
    +### Export users
    +
    +```
    +client.export_users_as_md("/tmp/taigawiki")
    +```
    +### Export circles
    +```
    +client.export_circles_as_md("/tmp/taigawiki")
    +```
    +
    +### Export users and circles periodically
    +
    +To export users and circles periodically each 10 minutes
    +```
    +client.export_as_md_periodically("/tmp/taigawiki", period= 600)
    +```
    +> **period** use seconds as time unit.
    +
    +### Export objects as yaml
    +To export All objects as yaml all you need is
    +
    +```
    +client.export_as_yaml("/tmp/exported_taiga_dir")
    +```
    +
    +This will export resources (users, projects, issues, stories, tasks) in `/tmp/exported_taiga_dir/$object_type/$object_id.yaml
    +
    +## Importing
    +
    +### Importing from yaml files
    +
    +To import from yaml files _files which exported using export_as_yaml_
    +
    +```
    +client.import_from_yaml("/tmp/exported_taiga_dir")
    +```
    +This will import resources (projects, issues, stories, tasks) as a new instance _import basic info till now_
    +
    +## Operations
    +
    +### Move a story to a project
    +
    +```
    +client.move_story_to_circle(789, 123) # story id, project id
    +```
    +
    +### Copy and Move Issue using project object
    +```
    +project_object.copy_issue(issue_id_or_issue_object, project_id_or_project_object)
    +project_object.move_issue(issue_id_or_issue_object, project_id_or_project_object)
    +```
    +> Keep in mind that move will delete the issue from the original project
    +
    +### Resources urls
    +All of resources e.g (user, issue, user_story, circle, task) have `url, as_md and as_yaml` properties
    +"""
    +
    +import copy
    +from collections import defaultdict
    +from functools import lru_cache
    +from textwrap import dedent
    +import dateutil
    +import dateutil.utils
    +import gevent
    +from gevent.event import Event
    +import yaml
    +from jumpscale.clients.base import Client
    +from jumpscale.clients.taiga.models import (
    +    Circle,
    +    CircleIssue,
    +    CircleStory,
    +    CircleTask,
    +    CircleUser,
    +    FunnelCircle,
    +    ProjectCircle,
    +    TeamCircle,
    +)
    +from jumpscale.core.base import fields
    +from jumpscale.loader import j
    +
    +from taiga import TaigaAPI
    +from taiga.exceptions import TaigaRestException
    +from taiga.models.models import Milestones
    +from pathlib import Path
    +
    +
    +class TaigaClient(Client):
    +    def credential_updated(self, value):
    +        self._api = None
    +
    +    host = fields.String(default="https://projects.threefold.me")
    +    username = fields.String(on_update=credential_updated)
    +    password = fields.Secret(on_update=credential_updated)
    +    token = fields.Secret(on_update=credential_updated)
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._api = None
    +        self.text = ""
    +
    +    def __hash__(self):
    +        return hash(str(self))
    +
    +    @property
    +    def api(self):
    +        if not self._api:
    +            api = TaigaAPI(host=self.host)
    +            if self.token:
    +                api.token = self.token
    +            else:
    +                if not self.username or not self.password:
    +                    raise j.exceptions.Runtime("Token or username and password are required")
    +                api.auth(self.username, self.password)
    +            self._api = api
    +        return self._api
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_project(self, project_id):
    +        return self.api.projects.get(project_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_milestone(self, milestone_id):
    +        if milestone_id:
    +            return self.api.milestones.get(milestone_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_priority(self, priority_id):
    +        return self.api.priorities.get(priority_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_assignee(self, assignee_id):
    +        return CircleUser(self, self.api.users.get(assignee_id))
    +
    +    _get_user_by_id = _get_assignee
    +
    +    def _get_users_by_ids(self, ids=None):
    +        ids = ids or []
    +        return [self._get_user_by_id(x) for x in ids]
    +
    +    def _get_issues_by_ids(self, ids=None):
    +        ids = ids or []
    +        return [self._get_issue_by_id(x) for x in ids]
    +
    +    def _get_task_by_id(self, id):
    +        return self.api.tasks.get(id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_issue_status(self, status_id):
    +        return self.api.issue_statuses.get(status_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_user_stories_status(self, status_id):
    +        return self.api.user_story_statuses.get(status_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_task_status(self, status_id):
    +        return self.api.task_statuses.get(status_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_user_id(self, username):
    +        user = self.api.users.list(username=username)
    +        if user:
    +            user = user[0]
    +            return user.id
    +        else:
    +            raise j.exceptions.Input(f"Couldn't find user with username: {username}")
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_user_by_name(self, username):
    +        theid = self._get_user_id(username)
    +        return self._get_user_by_id(theid)
    +
    +    def get_issue_custom_fields(self, id):
    +        """Get Issue Custom fields
    +
    +        Args:
    +            id (int): Issue id
    +
    +        Returns:
    +            List: List of dictionaries {name: "custom field name", value: {values as dict}}
    +        """
    +        issue = self.api.issues.get(id)
    +        issue_attributes = issue.get_attributes()["attributes_values"]
    +        project_attributes = self._get_project(issue.project).list_issue_attributes()
    +        custom_fields = []
    +        for p_attr in project_attributes:
    +            for k, value in issue_attributes.items():
    +                if p_attr.id == int(k):
    +                    try:
    +                        custom_fields.append({"name": p_attr.name, "value": yaml.full_load(value)})
    +                    except:
    +                        custom_fields.append({"name": p_attr.name, "value": value})
    +                    break
    +
    +        return custom_fields
    +
    +    def get_story_custom_fields(self, id):
    +        """Get User_Story Custom fields
    +
    +        Args:
    +            id (int): User_Story id
    +
    +        Returns:
    +            List: List of dictionaries {name: "custom field name", value: {values as dict}}
    +        """
    +        user_story = self.api.user_stories.get(id)
    +        user_story_attributes = user_story.get_attributes()["attributes_values"]
    +        project_attributes = self._get_project(user_story.project).list_user_story_attributes()
    +        custom_fields = []
    +        for p_attr in project_attributes:
    +            for k, value in user_story_attributes.items():
    +                if p_attr.id == int(k):
    +                    try:
    +                        custom_fields.append({"name": p_attr.name, "value": yaml.full_load(value)})
    +                    except:
    +                        custom_fields.append({"name": p_attr.name, "value": value})
    +                    break
    +
    +        return custom_fields
    +
    +    def get_user_circles(self, username):
    +        """Get circles owned by user
    +
    +        Args:
    +            username (str): Name of the user
    +        """
    +        user_id = self._get_user_id(username)
    +        circles = self.api.projects.list(member=user_id)
    +        user_circles = []
    +        for circle in circles:
    +            if circle.owner["id"] == user_id:
    +                user_circles.append(self._resolve_object(circle))
    +        return user_circles
    +
    +    def get_circles_issues(self, project_id):
    +        """Get all issues in a circle/project
    +
    +        Args:
    +            project_id (int): id of the circle/project
    +
    +        Raises:
    +            j.exceptions.NotFound: if couldn't find circle with specified id
    +        """
    +        try:
    +            circle = self.api.projects.get(project_id)
    +        except TaigaRestException:
    +            raise j.exceptions.NotFound(f"Couldn't find project with id: {project_id}")
    +
    +        circle_issues = []
    +        for issue in circle.list_issues():
    +            issue.project = self._get_project(issue.project)
    +            issue.milestone = self._get_milestone(issue.milestone)
    +            issue.priority = self._get_priority(issue.priority)
    +            issue.assignee = self._get_assignee(issue.assigned_to)
    +            issue.status = self._get_issue_status(issue.status)
    +            circle_issues.append(issue)
    +        return circle_issues
    +
    +    def get_user_stories(self, username):
    +        """Get all stories of a user
    +
    +        Args:
    +            username (str): Name of the user
    +        """
    +        user_id = self._get_user_id(username)
    +        user_stories = self.api.user_stories.list(assigned_to=user_id)
    +        user_stories = []
    +        for user_story in user_stories:
    +            # user_story.project = self._get_project(user_story.project)
    +            # user_story.milestone = self._get_milestone(user_story.milestone)
    +            user_story.status = self._get_user_stories_status(user_story.status)
    +            user_stories.append(user_story)
    +        return user_stories
    +
    +    def get_user_tasks(self, username):
    +        """Get all tasks of a user
    +
    +        Args:
    +            username (str): Name of the user
    +        """
    +        user_id = self._get_user_id(username)
    +        user_tasks = self.api.tasks.list(assigned_to=user_id)
    +        user_tasks = []
    +        for user_task in user_tasks:
    +            # user_task.project = self._get_project(user_task.project)
    +            # user_task.milestone = self._get_milestone(user_task.milestone)
    +            user_task.status = self._get_task_status(user_task.status)
    +            user_tasks.append(self._resolve_object(user_task))
    +
    +        return user_tasks
    +
    +    def move_story_to_circle(self, story_id, project_id):
    +        """Moves a story to another circle/project
    +
    +        Args:
    +            story_id (int): User story id
    +            project_id (int): circle/project id
    +
    +        Raises:
    +            j.exceptions.NotFound: No user story with specified id found
    +            j.exceptions.NotFound: No project with specified id found
    +            j.exceptions.Runtime: [description]
    +
    +        Returns:
    +            int: New id of the migrated user story
    +        """
    +
    +        def _get_project_status(project_statuses, status):
    +            for project_status in project_statuses:
    +                if project_status.name == status:
    +                    return project_status.id
    +
    +        try:
    +            user_story = self.api.user_stories.get(story_id)
    +        except TaigaRestException:
    +            raise j.exceptions.NotFound(f"Couldn't find user story with id: {story_id}")
    +
    +        project_stories_statuses = self.api.user_story_statuses.list(project=project_id)
    +        status = self._get_user_stories_status(user_story.status)
    +        story_status_id = _get_project_status(project_stories_statuses, status)
    +
    +        try:
    +            migrate_story = self.api.user_stories.create(
    +                project=project_id,
    +                subject=user_story.subject,
    +                assigned_to=user_story.assigned_to,
    +                milestone=user_story.milestone,
    +                status=story_status_id,
    +                tags=user_story.tags,
    +            )
    +        except TaigaRestException:
    +            raise j.exceptions.NotFound(f"No project with id: {project_id} found")
    +        try:
    +            comments = self.api.history.user_story.get(story_id)
    +            comments = sorted(comments, key=lambda c: dateutil.parser.isoparse(c["created_at"]))
    +
    +            for comment in comments:
    +                migrate_story.add_comment(comment["comment_html"])
    +
    +            project_tasks_statuses = self.api.task_statuses.list(project=project_id)
    +            for task in user_story.list_tasks():
    +                status = self._get_task_status(task.status)
    +                task_status_id = _get_project_status(project_tasks_statuses, status)
    +                migrate_task = migrate_story.add_task(
    +                    subject=task.subject,
    +                    status=task_status_id,
    +                    due_date=task.due_date,
    +                    milestone=task.milestone,
    +                    assigned_to=task.assigned_to,
    +                    tags=task.tags,
    +                    project=migrate_story.project,
    +                    user_story=migrate_story.id,
    +                )
    +                comments = self.api.history.task.get(migrate_task.id)
    +                comments = sorted(comments, key=lambda c: dateutil.parser.isoparse(c["created_at"]))
    +
    +                for comment in comments:
    +                    migrate_task.add_comment(comment["comment_html"])
    +
    +        except Exception as e:
    +            self.api.user_stories.delete(migrate_story.id)
    +            raise j.exceptions.Runtime(f"Failed to migrate story error was: {str(e)}")
    +
    +        self.api.user_stories.delete(story_id)
    +        return migrate_story.id
    +
    +    def list_all_issues(self, username="", full_info=False):
    +        """
    +        List all issues for specific user if you didn't pass user_id will list all the issues
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            username (str): username.
    +            full_info (bool): flag used to get object with full info. Defaults to False.
    +
    +        Returns:
    +            List: List of taiga.models.models.Issue.
    +        """
    +        if username:
    +            user_id = self._get_user_id(username)
    +            if not full_info:
    +                return [CircleIssue(self, self._resolve_object(x)) for x in self.api.issues.list(assigned_to=user_id)]
    +            else:
    +                return [CircleIssue(self, self.api.issues.get(x.id)) for x in self.api.issues.list(assigned_to=user_id)]
    +        else:
    +            if not full_info:
    +                return [CircleIssue(self, self._resolve_object(x)) for x in self.api.issues.list()]
    +            else:
    +                return [CircleIssue(self, self.api.issues.get(x.id)) for x in self.api.issues.list()]
    +
    +    def list_all_tasks(self, username="", full_info=False):
    +        """
    +        List all tasks for specific user if you didn't pass user_id will list all the tasks
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            username (str): username.
    +            full_info (bool): flag used to get object with full info. Defaults to False.
    +
    +        Returns:
    +            List: List of taiga.models.models.Task.
    +        """
    +        if username:
    +            user_id = self._get_user_id(username)
    +            if not full_info:
    +                return [CircleTask(self, self._resolve_object(x)) for x in self.api.tasks.list(assigned_to=user_id)]
    +            else:
    +                return [CircleTask(self, self.api.tasks.get(x.id)) for x in self.api.tasks.list(assigned_to=user_id)]
    +        else:
    +            if not full_info:
    +                return [CircleTask(self, self._resolve_object(x)) for x in self.api.tasks.list()]
    +            else:
    +                return [CircleTask(self, self.api.tasks.get(x.id)) for x in self.api.tasks.list()]
    +
    +    def list_all_projects(self, full_info=False):
    +        """
    +        List all projects
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            full_info(bool): flag used to get object with full info. Defaults to False.
    +
    +        Returns:
    +            List: List of taiga.models.models.Project.
    +        """
    +        if not full_info:
    +            return [Circle(self, self._resolve_object(x)) for x in self.api.projects.list()]
    +        else:
    +            return [Circle(self, self.api.projects.get(x.id)) for x in self.api.projects.list()]
    +
    +    def list_all_active_projects(self, full_info=False):
    +        """
    +        List all projects not starting with "ARHCIVE"
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            full_info (bool): [description]. Defaults to False.
    +
    +        Returns:
    +            [type]: [description]
    +        """
    +        return [
    +            Circle(self, p)
    +            for p in self.list_projects_by(lambda x: not x.name.startswith("ARCHIVE_"), full_info=full_info)
    +        ]
    +
    +    def list_all_milestones(self):
    +        """
    +        List all milestones
    +
    +        Returns:
    +            List: List of taiga.models.models.Milestone.
    +        """
    +        return [self._resolve_object(x) for x in self.api.milestones.list()]
    +
    +    def list_all_user_stories(self, username="", full_info=False):
    +        """
    +        List all user stories for specific user if you didn't pass user_id will list all the available user stories
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            username (str): username.
    +            full_info(bool): flag used to get object with full info. Defaults to False
    +
    +        Returns:
    +            List: List of CircleStory.
    +        """
    +        if username:
    +            user_id = self._get_user_id(username)
    +            if not full_info:
    +                return [
    +                    CircleStory(self, self._resolve_object(x)) for x in self.api.user_stories.list(assigned_to=user_id)
    +                ]
    +            else:
    +                return [
    +                    CircleStory(self, self.api.user_stories.get(x.id))
    +                    for x in self.api.user_stories.list(assigned_to=user_id)
    +                ]
    +        else:
    +            if not full_info:
    +                return [CircleStory(self, self._resolve_object(x)) for x in self.api.user_stories.list()]
    +            else:
    +                return [CircleStory(self, self.api.user_stories.get(x.id)) for x in self.api.user_stories.list()]
    +
    +    def list_all_users(self, full_info=False):
    +        """
    +        List all user stories for specific user if you didn't pass user_id will list all the available user stories
    +        HINT: Using full_info will take a longer time
    +        Args:
    +            username (str): username.
    +            full_info(bool): flag used to get object with full info. Defaults to False
    +
    +        Returns:
    +            List: List of CircleUser.
    +        """
    +        circles = self.list_all_projects()
    +        users = set()
    +        for c in circles:
    +            for m in c.members:
    +                users.add(m)
    +
    +        return [CircleUser(self, self._get_user_by_id(uid)) for uid in users]
    +
    +    def get_issue_by_id(self, issue_id):
    +        """Get issue
    +        Args:
    +            issue_id: the id of the desired issue
    +
    +        Returns:
    +            Issue object: issue
    +        """
    +        return CircleIssue(self, self.api.issues.get(issue_id))
    +
    +    def _resolve_object(self, obj):
    +        resolvers = {
    +            "owners": self._get_users_by_ids,
    +            "watchers": self._get_users_by_ids,
    +            "members": self._get_users_by_ids,
    +            "project": self._get_project,
    +            "circle": self._get_project,
    +            "milestone": self._get_milestone,
    +            "task_status": self._get_task_status,
    +            "assigned_to": self._get_user_by_id,
    +            "owner": self._get_user_by_id,
    +            "issues": self._get_issues_by_ids,
    +            "tasks": self._get_task_by_id,
    +        }
    +        newobj = copy.deepcopy(obj)
    +        for k in dir(newobj):
    +            v = getattr(newobj, k)
    +            if isinstance(v, int) or isinstance(v, list) and v and isinstance(v[0], int):
    +                if k in resolvers:
    +                    resolved = None
    +                    resolver = resolvers[k]
    +                    try:
    +                        copied_v = copy.deepcopy(v)
    +                        resolved = lambda: resolver(copied_v)
    +
    +                        if isinstance(v, list):
    +                            setattr(newobj, f"{k}_objects", resolved)
    +                        else:
    +                            setattr(newobj, f"{k}_object", resolved)
    +
    +                    except Exception as e:
    +                        import traceback
    +
    +                        traceback.print_exc()
    +
    +                        j.logger.error(f"error {e}")
    +
    +        return newobj
    +
    +    def list_projects_by(self, fn=lambda x: True, full_info=False):
    +        return [p for p in self.list_all_projects(full_info=full_info) if fn(p)]
    +
    +    def list_team_circles(self):
    +        return [TeamCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("TEAM_"))]
    +
    +    def list_project_circles(self):
    +        return [ProjectCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("PROJECT_"))]
    +
    +    def list_funnel_circles(self):
    +        return [FunnelCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("FUNNEL_"))]
    +
    +    def validate_custom_fields(self, attributes):
    +        """Validate custom fields values to match our requirements
    +
    +        Args:
    +            attributes (List): Output from get_issue/story_custom_fields functions
    +
    +        Raises:
    +            j.exceptions.Validation: Raise validation exception if any input not valid
    +
    +        Returns:
    +            bool: Return True if no exception raised and print logs
    +        """
    +
    +        for attr in attributes:
    +            name = attr.get("name")
    +            value = attr.get("value")
    +
    +            period = value.get("period", "onetime")
    +            duration = value.get("duration", 1)
    +            amount = value.get("amount", 0)
    +            currency = value.get("currency", "eur")
    +            start_date = value.get("start_date", f"{dateutil.utils.today().month}:{dateutil.utils.today().year}",)
    +            confidence = value.get("confidence", 100)
    +            user = value.get("user")
    +            part = value.get("part", "0%")
    +            type = value.get("type", "revenue")
    +
    +            if name not in ["bookings", "commission"]:
    +                raise j.exceptions.Validation(
    +                    f'Name: ({name}) is unknown custom field, please select one of the following ["bookings", "commission"]'
    +                )
    +
    +            if period not in ["onetime", "month", "year"]:
    +                raise j.exceptions.Validation(
    +                    f'Period: ({period}) not found, please select one of following ["onetime", "month", "year"]'
    +                )
    +
    +            if duration < 1 or duration > 120:
    +                raise j.exceptions.Validation(f"Duration: ({duration}) is not in range, please select it from 1 to 120")
    +
    +            if not isinstance(amount, int):
    +                raise j.exceptions.Validation(f"Amount: ({amount}) is not integer, please add int value")
    +
    +            if currency.replace(" ", "").lower() not in [
    +                "usd",
    +                "chf",
    +                "eur",
    +                "gbp",
    +                "egp",
    +            ]:
    +                raise j.exceptions.Validation(
    +                    f'Currency: ({currency}) is not supported, please use one of the following currencies ["usd", "chf", "eur", "gbp", "egp"]'
    +                )
    +            try:
    +                date = start_date.split(":")
    +                month = int(date[0])
    +                year = int(date[1]) if len(date) > 1 else dateutil.utils.today().year
    +                if month < 1 or month > 12:
    +                    raise j.exceptions.Validation(
    +                        "Please use values from 1 to 12 in Month field, follow format like MONTH:YEAR as 11:2020 or MONTH as 11"
    +                    )
    +            except ValueError as e:
    +                raise j.exceptions.Validation(
    +                    "Please use numeric date with the following format MONTH:YEAR as 11:2020 or MONTH as 11"
    +                )
    +            except AttributeError as e:
    +                pass  # Will check what happen if start_date not provide
    +
    +            if confidence % 10 != 0:
    +                j.exceptions.Validation(f"Confidence: ({confidence}) not multiple of 10, it must be multiple of 10")
    +
    +            part_tmp = part.replace("%", "")
    +            if user != None and user not in self.list_all_users():
    +                raise j.exceptions.Validation(f"User: ({user}) is not found")
    +
    +            if int(part_tmp) < 0 or int(part_tmp) > 100:
    +                j.exceptions.Validation(f"Part: ({part}) is a not a valid percentage, it must be from 0% to 100%")
    +
    +            if type not in ["revenue", "booking"]:
    +                raise j.exceptions.Validation(
    +                    f'Type: ({type}) is not supported type, please choose one of the following ["revenue" , "booking"]'
    +                )
    +
    +            j.logger.info(f"Attribute: {name} passed")
    +
    +        return True
    +
    +    def _create_new_circle(
    +        self,
    +        name,
    +        type_="team",
    +        description="desc",
    +        severities=None,
    +        issues_statuses=None,
    +        priorities=None,
    +        issues_types=None,
    +        user_stories_statuses=None,
    +        tasks_statuses=None,
    +        custom_fields=None,
    +        **attrs,
    +    ):
    +        severities = severities or ["Low", "Mid", "High"]
    +        priorities = priorities or [
    +            "Wishlist",
    +            "Minor",
    +            "Normal",
    +            "Important",
    +            "Critical",
    +        ]
    +        issues_statuses = issues_statuses or [
    +            "New",
    +            "In progress",
    +            "Ready for test",
    +            "Closed",
    +            "Needs Info",
    +            "Rejected",
    +            "Postponed",
    +        ]
    +        issues_types = issues_types or []
    +        user_stories_statuses = user_stories_statuses or []
    +        tasks_statuses = tasks_statuses or []
    +        custom_fields = custom_fields or []
    +
    +        type_ = type_.upper()
    +        project_name = f"{type_}_{name}"
    +        p = self.api.projects.create(project_name, description=description)
    +        for t in tasks_statuses:
    +            try:
    +                p.add_task_status(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping task {t} {e}")
    +
    +        for t in priorities:
    +            try:
    +                p.add_priority(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping prio {t} {e}")
    +
    +        for t in severities:
    +            try:
    +                p.add_severity(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping sever {t} {e}")
    +
    +        for t in issues_statuses:
    +            try:
    +                p.add_issue_status(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping status {t} {e}")
    +
    +        for t in user_stories_statuses:
    +            try:
    +                p.add_user_story_status(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping user status {t} {e}")
    +
    +        for t in issues_types:
    +            try:
    +                p.add_issue_type(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping issue type {t} {e}")
    +
    +        for t in custom_fields:
    +            try:
    +                p.add_issue_attribute(t)
    +                p.add_user_story_attribute(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping custom field type {t} {e}")
    +
    +        return p
    +
    +    def create_new_project_circle(
    +        self, name, description="", **attrs,
    +    ):
    +        """Creates a new project circle.
    +
    +        Args:
    +            name (str): circle name
    +            description (str, optional): circle description. Defaults to "".
    +
    +        Returns:
    +            [ProjectCircle]: Project circle
    +        """
    +
    +        attrs = {
    +            "is_backlog_activated": False,
    +            "is_issues_activated": True,
    +            "is_kanban_activated": True,
    +            "is_private": False,
    +            "is_wiki_activated": True,
    +        }
    +        issues_types = ["Bug", "Question", "Enhancement"]
    +        severities = ["Wishlist", "Minor", "Normal", "Important", "Critical"]
    +        priorities = None
    +
    +        story_statuses = [
    +            "New",
    +            "to-start",
    +            "in-progress",
    +            "Blocked",
    +            "Implemented",
    +            "Verified",
    +            "Archived",
    +        ]
    +        item_statuses = ["New", "to-start", "in-progress", "Blocked", "Done"]
    +        issues_statuses = [
    +            "New",
    +            "to-start",
    +            "in-progress",
    +            "Blocked",
    +            "Implemented",
    +            "Closed",
    +            "Rejected",
    +            "Postponed",
    +            "Archived",
    +        ]
    +
    +        return ProjectCircle(
    +            self,
    +            self._create_new_circle(
    +                name,
    +                type_="project",
    +                description=description,
    +                severities=severities,
    +                issues_statuses=issues_statuses,
    +                priorities=priorities,
    +                issues_types=issues_types,
    +                user_stories_statuses=story_statuses,
    +                tasks_statuses=item_statuses,
    +                **attrs,
    +            ),
    +        )
    +
    +    def create_new_team_circle(self, name, description="", **attrs):
    +        """Creates a new team circle. using sprints & timeline (does not use kanban)
    +
    +        Args:
    +            name (str): circle name
    +            description (str, optional): circle description. Defaults to "".
    +            severities (List[str], optional): list of strings to represent severities. Defaults to None.
    +            issues_statuses (List[str], optional): list of strings to represent issues_stauses. Defaults to None.
    +            priorities (List[str], optional): list of strings to represent priorities. Defaults to None.
    +            issues_types (List[str], optional): list of strings to represent issues types. Defaults to None.
    +            user_stories_statuses (List[str], optional): list of strings to represent user stories. Defaults to None.
    +            tasks_statuses (List[str], optional): list of strings to represent task statuses. Defaults to None.
    +
    +        Returns:
    +            [TeamCircle]: team circle
    +        """
    +
    +        attrs = {
    +            "is_backlog_activated": True,
    +            "is_issues_activated": True,
    +            "is_kanban_activated": False,
    +            "is_private": False,
    +            "is_wiki_activated": True,
    +        }
    +        issues_types = ["Bug", "Question", "Enhancement"]
    +        severities = ["Wishlist", "Minor", "Normal", "Important", "Critical"]
    +        priorities = None
    +
    +        story_statuses = [
    +            "New",
    +            "to-start",
    +            "in-progress",
    +            "Blocked",
    +            "Implemented",
    +            "Verified",
    +            "Archived",
    +        ]
    +        item_statuses = ["New", "to-start", "in-progress", "Blocked", "Done"]
    +        issues_statuses = [
    +            "New",
    +            "to-start",
    +            "in-progress",
    +            "Blocked",
    +            "Implemented",
    +            "Closed",
    +            "Rejected",
    +            "Postponed",
    +            "Archived",
    +        ]
    +
    +        return TeamCircle(
    +            self,
    +            self._create_new_circle(
    +                name,
    +                type_="team",
    +                description=description,
    +                severities=severities,
    +                issues_statuses=issues_statuses,
    +                priorities=priorities,
    +                issues_types=issues_types,
    +                user_stories_statuses=story_statuses,
    +                tasks_statuses=item_statuses,
    +                **attrs,
    +            ),
    +        )
    +
    +    def create_new_funnel_circle(self, name, description="", **attrs):
    +        """Creates a new funnel circle. using sprints & timeline (does not use kanban)
    +
    +        Args:
    +            name (str): circle name
    +            description (str, optional): circle description. Defaults to "".
    +
    +        Returns:
    +            [FunnelCircle]: funnel circle
    +        """
    +        attrs = {
    +            "is_backlog_activated": False,
    +            "is_issues_activated": True,
    +            "is_kanban_activated": True,
    +            "is_private": False,
    +            "is_wiki_activated": True,
    +        }
    +
    +        severities = ["unknown", "low", "25%", "50%", "75%", "90%"]
    +        priorities = ["Low", "Normal", "High"]
    +        issues_types = "opportunity"
    +
    +        issues_statuses = [
    +            "New",
    +            "Interested",
    +            "Deal",
    +            "Blocked",
    +            "NeedInfo",
    +            "Lost",
    +            "Postponed",
    +            "Won",
    +        ]
    +        story_statuses = [
    +            "New",
    +            "Proposal",
    +            "Contract",
    +            "Blocked",
    +            "NeedInfo",
    +            "Closed",
    +        ]
    +        task_statuses = ["New", "In progress", "Verification", "Needs info", "Closed"]
    +
    +        custom_fields = ["bookings", "commission"]
    +
    +        return FunnelCircle(
    +            self,
    +            self._create_new_circle(
    +                name,
    +                type_="funnel",
    +                description=description,
    +                severities=severities,
    +                issues_statuses=issues_statuses,
    +                priorities=priorities,
    +                issues_types=issues_types,
    +                user_stories_statuses=story_statuses,
    +                tasks_statuses=task_statuses,
    +                custom_fields=custom_fields,
    +                **attrs,
    +            ),
    +        )
    +
    +    def export_circles_as_md(self, wikipath="/tmp/taigawiki", modified_only=True, full_info=False):
    +        """export circles into {wikipath}/src/circles
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            wikipath (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +            full_info (bool): export object with full info. Defaults to False
    +        """
    +        path = j.sals.fs.join_paths(wikipath, "src", "circles")
    +
    +        j.sals.fs.mkdirs(path)
    +        circles = self.list_all_active_projects(full_info=full_info)
    +
    +        def write_md_for_circle(circle):
    +            circle_md = circle.as_md
    +            circle_mdpath = j.sals.fs.join_paths(path, f"{circle.clean_name}.md")
    +            if not (
    +                modified_only and j.sals.fs.exists(circle_mdpath) and j.sals.fs.read_ascii(circle_mdpath) == circle_md
    +            ):
    +                j.sals.fs.write_ascii(circle_mdpath, circle_md)
    +
    +        circles_mdpath = j.sals.fs.join_paths(path, "circles.md")
    +        circles_mdcontent = "# circles\n\n"
    +        for c in circles:
    +            circles_mdcontent += f"- [{c.name}](./{c.clean_name}.md)\n"
    +
    +        j.sals.fs.write_ascii(circles_mdpath, circles_mdcontent)
    +
    +        greenlets = [gevent.spawn(write_md_for_circle, gcircle_obj) for gcircle_obj in circles]
    +        gevent.joinall(greenlets)
    +
    +    def export_users_as_md(self, wikipath="/tmp/taigawiki", modified_only=True, full_info=False):
    +        """export users into {wikipath}/src/users
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            wikipath (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +            modified_only (bool): export moidified objects only
    +            full_info (bool): export object with full info. Defaults to False
    +        """
    +
    +        path = j.sals.fs.join_paths(wikipath, "src", "users")
    +        j.sals.fs.mkdirs(path)
    +        users_objects = self.list_all_users(full_info=full_info)
    +
    +        users_mdpath = j.sals.fs.join_paths(path, "users.md")
    +        users_mdcontent = "# users\n\n"
    +
    +        def write_md_for_user(user):
    +            user_md = user.as_md
    +            user_mdpath = j.sals.fs.join_paths(path, f"{user.clean_name}.md")
    +            if not (modified_only and j.sals.fs.exists(user_mdpath) and j.sals.fs.read_ascii(user_mdpath) == user_md):
    +                j.sals.fs.write_ascii(user_mdpath, user_md)
    +
    +        for u in users_objects:
    +            users_mdcontent += f"- [{u.username}](./{u.clean_name}.md)\n"
    +
    +        j.sals.fs.write_ascii(users_mdpath, users_mdcontent)
    +
    +        greenlets = [gevent.spawn(write_md_for_user, guser_obj) for guser_obj in users_objects]
    +        gevent.joinall(greenlets)
    +
    +    def export_as_md(self, wiki_path="/tmp/taigawiki", modified_only: bool = True, full_info=False):
    +        """export taiga instance into a wiki  showing users and circles
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            wiki_src_path (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +            modified_only (bool): write modified objects only. Defaults to True
    +            full_info (bool): export object with full info. Defaults to False
    +        """
    +        j.logger.info("Start Exporting Wiki ...")
    +        gs = []
    +        gs.append(gevent.spawn(self.export_circles_as_md, wiki_path, modified_only, full_info))
    +        gs.append(gevent.spawn(self.export_users_as_md, wiki_path, modified_only, full_info))
    +        gevent.joinall(gs)
    +
    +        template_file = j.sals.fs.join_paths(Path(__file__).parent, "template.html")
    +        index_html_path = j.sals.fs.join_paths(wiki_path, "src", "index.html")
    +        readme_md_path = j.sals.fs.join_paths(wiki_path, "src", "README.md")
    +        sidebar_md_path = j.sals.fs.join_paths(wiki_path, "src", "_sidebar.md")
    +        content = dedent(
    +            f"""
    +            # Taiga overview
    +
    +            - [circles](./circles/circles.md)
    +            - [users](./users/users.md)
    +        """
    +        )
    +        j.sals.fs.write_ascii(readme_md_path, content)
    +        j.sals.fs.write_ascii(sidebar_md_path, content)
    +        j.sals.fs.copy_file(template_file, index_html_path)
    +        j.logger.info(f"Exported at {wiki_path}")
    +
    +    def export_as_md_periodically(
    +        self, wiki_path="/tmp/taigawiki", period: int = 300, modified_only: bool = True, full_info=False
    +    ):
    +        """export taiga instance into a wiki  showing users and circles periodically
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            wiki_path (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +            period (int): Time to wait between each export in "Seconds". Defaults to 300 (5 Min).
    +            modified_only (bool): write modified objects only.. Defaults to True.
    +            full_info (bool): export object with full info. Defaults to False
    +
    +        """
    +        repeater = Event()
    +        while True:
    +            j.logger.info("Start Exporting ....")
    +            self.export_as_md(wiki_path, modified_only, full_info)
    +            j.logger.info(f"Exported at {wiki_path}")
    +            repeater.wait(period)
    +
    +    def export_as_yaml(self, export_dir="/tmp/export_dir", full_info=False):
    +        """export taiga instance [Circle, Story, Issue, Task , User] into a yaml files
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            export_dir (str, optional): [description]. Defaults to "/tmp/export_dir".
    +            full_info (bool): export object with full info. Defaults to False
    +        """
    +
    +        def _export_objects_to_dir(objects_dir, objects_fun, full_info):
    +            j.sals.fs.mkdirs(objects_dir)
    +            objects = objects_fun(full_info=full_info)
    +            for obj in objects:
    +                try:
    +                    outpath = j.sals.fs.join_paths(objects_dir, f"{obj.id}.yaml")
    +                    j.sals.fs.write_ascii(outpath, obj.as_yaml)
    +
    +                except Exception as e:
    +                    import traceback
    +
    +                    traceback.print_exc()
    +                    j.logger.error(e)
    +                    j.logger.error(f"{type(obj)}: {obj.id}")
    +
    +        projects_path = j.sals.fs.join_paths(export_dir, "projects")
    +        stories_path = j.sals.fs.join_paths(export_dir, "stories")
    +        issues_path = j.sals.fs.join_paths(export_dir, "issues")
    +        tasks_path = j.sals.fs.join_paths(export_dir, "tasks")
    +        # Milestones is not one of our model objects
    +        # milestones_path = j.sals.fs.join_paths(export_dir, "milestones")
    +        users_path = j.sals.fs.join_paths(export_dir, "users")
    +
    +        def on_err(*args, **kwargs):
    +            print("err, ", args, kwargs)
    +
    +        j.logger.info("Start Export as YAML")
    +
    +        gs = []
    +        gs.append(gevent.spawn(_export_objects_to_dir, projects_path, self.list_all_active_projects, full_info))
    +        gs.append(gevent.spawn(_export_objects_to_dir, stories_path, self.list_all_user_stories, full_info))
    +        gs.append(gevent.spawn(_export_objects_to_dir, issues_path, self.list_all_issues, full_info))
    +        # Milestones is not one of our model objects
    +        # gs.append(gevent.spawn(_export_objects_to_dir, milestones_path, self.list_all_milestones)
    +        gs.append(gevent.spawn(_export_objects_to_dir, users_path, self.list_all_users, full_info))
    +        gs.append(gevent.spawn(_export_objects_to_dir, tasks_path, self.list_all_tasks, full_info))
    +        gevent.joinall(gs)
    +
    +        j.logger.info("Finish Export as YAML")
    +
    +    def import_from_yaml(self, import_dir="/tmp/export_dir"):
    +        """Import Circle with all stories, issues and tasks from yaml files
    +
    +        Args:
    +            import_dir (str): import directory path. Defaults to "/tmp/export_dir".
    +        """
    +        # Helper Functions
    +        def check_by_name(list_obj, name_to_find):
    +            for obj in list_obj:
    +                if obj.name == name_to_find:
    +                    return obj.id
    +
    +            return list_obj[0].id
    +
    +        def import_circle(self, yaml_obj):
    +            circle = None
    +            # Funnel Circle
    +            if yaml_obj["basic_info"]["name"].lower() == "funnel":
    +                circle = self.create_new_funnel_circle(
    +                    yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +                )
    +            # Team Circle
    +            elif yaml_obj["basic_info"]["name"].lower() == "team":
    +                circle = self.create_new_team_circle(
    +                    yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +                )
    +            # Project Circle
    +            elif yaml_obj["basic_info"]["name"].lower() == "project":
    +                circle = self.create_new_project_circle(
    +                    yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +                )
    +            # Any Other Circle
    +            else:
    +                circle = self._create_new_circle(yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],)
    +                circle.is_backlog_activated = yaml_obj["modules"]["is_backlog_activated"]
    +                circle.is_issues_activated = yaml_obj["modules"]["is_issues_activated"]
    +                circle.is_kanban_activated = yaml_obj["modules"]["is_kanban_activated"]
    +                circle.is_wiki_activated = yaml_obj["modules"]["is_wiki_activated"]
    +
    +            circle.is_private = yaml_obj["basic_info"]["is_private"]
    +            circle.videoconferences = yaml_obj["modules"]["videoconferences"]
    +            for issue_attr in yaml_obj["issues_attributes"]:
    +                circle.add_issue_attribute(issue_attr)
    +            for us_attr in yaml_obj["stories_attributes"]:
    +                circle.add_user_story_attribute(us_attr)
    +            return circle
    +
    +        def import_story(circle_object, yaml_obj):
    +            status_id = check_by_name(circle_object.us_statuses, yaml_obj["status"]["name"])
    +
    +            story = circle_object.add_user_story(
    +                yaml_obj["basic_info"].get("subject"),
    +                tags=yaml_obj["basic_info"].get("tags"),
    +                description=yaml_obj["basic_info"].get("description", ""),
    +                client_requirement=yaml_obj["requirements"].get("client_requirement"),
    +                team_requirement=yaml_obj["requirements"].get("team_requirement"),
    +                is_blocked=yaml_obj["additional_info"].get("is_blocked"),
    +                due_date=yaml_obj["date"].get("due_date"),
    +                status=status_id,
    +            )
    +
    +            for field in yaml_obj.get("custom_fields", []):
    +                for attr in circle_object.list_user_story_attributes():
    +                    if attr.name == field["name"]:
    +                        story.set_attribute(attr.id, field["value"])
    +                        break
    +            return story
    +
    +        def import_issue(circle_object, yaml_obj):
    +            priority_id = check_by_name(circle_object.priorities, yaml_obj["priority"]["name"])
    +            status_id = check_by_name(circle_object.issue_statuses, yaml_obj["status"]["name"])
    +            type_id = check_by_name(circle_object.issue_types, yaml_obj["type"]["name"])
    +            severity_id = check_by_name(circle_object.severities, yaml_obj["severity"]["name"])
    +            issue = circle_object.add_issue(
    +                yaml_obj["basic_info"]["subject"],
    +                priority_id,
    +                status_id,
    +                type_id,
    +                severity_id,
    +                description=yaml_obj["basic_info"].get("description"),
    +            )
    +
    +            for field in yaml_obj["custom_fields"]:
    +                for attr in circle_object.list_issue_attributes():
    +                    if attr.name == field["name"]:
    +                        issue.set_attribute(attr.id, field["value"])
    +                        break
    +            return issue
    +
    +        def import_tasks(circle_object, story_object, yaml_obj):
    +            status_id = check_by_name(circle_object.task_statuses, yaml_obj["status"]["name"])
    +            task = story_object.add_task(
    +                yaml_obj["basic_info"].get("subject"),
    +                status_id,
    +                description=yaml_obj["basic_info"].get("description", ""),
    +                tags=yaml_obj["basic_info"].get("tags"),
    +            )
    +            return task
    +
    +        # Folders Path
    +        projects_path = j.sals.fs.join_paths(import_dir, "projects")
    +        stories_path = j.sals.fs.join_paths(import_dir, "stories")
    +        issues_path = j.sals.fs.join_paths(import_dir, "issues")
    +        tasks_path = j.sals.fs.join_paths(import_dir, "tasks")
    +
    +        # List of Files inside project Folder
    +        projects = j.sals.fs.os.listdir(projects_path)
    +
    +        for project_file in projects:
    +            if project_file.endswith(".yaml") or project_file.endswith(".yml"):
    +                with open(j.sals.fs.join_paths(projects_path, project_file)) as pf:
    +                    circle_yaml = yaml.full_load(pf)
    +                    circle_obj = import_circle(self, circle_yaml)
    +                    j.logger.info(f"<Circle {circle_obj.id} Created>")
    +
    +                    for story in circle_yaml["stories"]:
    +                        with open(j.sals.fs.join_paths(stories_path, f"{story}.yaml")) as sf:
    +                            story_yaml = yaml.full_load(sf)
    +                            story_obj = import_story(circle_obj, story_yaml)
    +                            j.logger.info(f"<Story {story_obj.id} Created in Circle {circle_obj.id}>")
    +
    +                            for task in story_yaml["tasks"]:
    +                                with open(j.sals.fs.join_paths(tasks_path, f"{task}.yaml")) as tf:
    +                                    task_yaml = yaml.full_load(tf)
    +                                    task_obj = import_tasks(circle_obj, story_obj, task_yaml)
    +                                    j.logger.info(f"<Task {task_obj.id} Created in Story {story_obj.id}>")
    +                                    task_obj.update()
    +                            story_obj.update()
    +
    +                    for issue in circle_yaml["issues"]:
    +                        with open(j.sals.fs.join_paths(issues_path, f"{issue}.yaml")) as isf:
    +                            issue_yaml = yaml.full_load(isf)
    +                            issue_obj = import_issue(circle_obj, issue_yaml)
    +                            j.logger.info(f"<Issue {issue_obj.id} Created in Circle {circle_obj.id}>")
    +                            issue_obj.update()
    +
    +                    circle_obj.update()
    +                    j.logger.info(f"<Circle {circle_obj.id} Imported with All Stories and Issues")
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class TaigaClient +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class TaigaClient(Client):
    +    def credential_updated(self, value):
    +        self._api = None
    +
    +    host = fields.String(default="https://projects.threefold.me")
    +    username = fields.String(on_update=credential_updated)
    +    password = fields.Secret(on_update=credential_updated)
    +    token = fields.Secret(on_update=credential_updated)
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._api = None
    +        self.text = ""
    +
    +    def __hash__(self):
    +        return hash(str(self))
    +
    +    @property
    +    def api(self):
    +        if not self._api:
    +            api = TaigaAPI(host=self.host)
    +            if self.token:
    +                api.token = self.token
    +            else:
    +                if not self.username or not self.password:
    +                    raise j.exceptions.Runtime("Token or username and password are required")
    +                api.auth(self.username, self.password)
    +            self._api = api
    +        return self._api
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_project(self, project_id):
    +        return self.api.projects.get(project_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_milestone(self, milestone_id):
    +        if milestone_id:
    +            return self.api.milestones.get(milestone_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_priority(self, priority_id):
    +        return self.api.priorities.get(priority_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_assignee(self, assignee_id):
    +        return CircleUser(self, self.api.users.get(assignee_id))
    +
    +    _get_user_by_id = _get_assignee
    +
    +    def _get_users_by_ids(self, ids=None):
    +        ids = ids or []
    +        return [self._get_user_by_id(x) for x in ids]
    +
    +    def _get_issues_by_ids(self, ids=None):
    +        ids = ids or []
    +        return [self._get_issue_by_id(x) for x in ids]
    +
    +    def _get_task_by_id(self, id):
    +        return self.api.tasks.get(id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_issue_status(self, status_id):
    +        return self.api.issue_statuses.get(status_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_user_stories_status(self, status_id):
    +        return self.api.user_story_statuses.get(status_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_task_status(self, status_id):
    +        return self.api.task_statuses.get(status_id)
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_user_id(self, username):
    +        user = self.api.users.list(username=username)
    +        if user:
    +            user = user[0]
    +            return user.id
    +        else:
    +            raise j.exceptions.Input(f"Couldn't find user with username: {username}")
    +
    +    @lru_cache(maxsize=2048)
    +    def _get_user_by_name(self, username):
    +        theid = self._get_user_id(username)
    +        return self._get_user_by_id(theid)
    +
    +    def get_issue_custom_fields(self, id):
    +        """Get Issue Custom fields
    +
    +        Args:
    +            id (int): Issue id
    +
    +        Returns:
    +            List: List of dictionaries {name: "custom field name", value: {values as dict}}
    +        """
    +        issue = self.api.issues.get(id)
    +        issue_attributes = issue.get_attributes()["attributes_values"]
    +        project_attributes = self._get_project(issue.project).list_issue_attributes()
    +        custom_fields = []
    +        for p_attr in project_attributes:
    +            for k, value in issue_attributes.items():
    +                if p_attr.id == int(k):
    +                    try:
    +                        custom_fields.append({"name": p_attr.name, "value": yaml.full_load(value)})
    +                    except:
    +                        custom_fields.append({"name": p_attr.name, "value": value})
    +                    break
    +
    +        return custom_fields
    +
    +    def get_story_custom_fields(self, id):
    +        """Get User_Story Custom fields
    +
    +        Args:
    +            id (int): User_Story id
    +
    +        Returns:
    +            List: List of dictionaries {name: "custom field name", value: {values as dict}}
    +        """
    +        user_story = self.api.user_stories.get(id)
    +        user_story_attributes = user_story.get_attributes()["attributes_values"]
    +        project_attributes = self._get_project(user_story.project).list_user_story_attributes()
    +        custom_fields = []
    +        for p_attr in project_attributes:
    +            for k, value in user_story_attributes.items():
    +                if p_attr.id == int(k):
    +                    try:
    +                        custom_fields.append({"name": p_attr.name, "value": yaml.full_load(value)})
    +                    except:
    +                        custom_fields.append({"name": p_attr.name, "value": value})
    +                    break
    +
    +        return custom_fields
    +
    +    def get_user_circles(self, username):
    +        """Get circles owned by user
    +
    +        Args:
    +            username (str): Name of the user
    +        """
    +        user_id = self._get_user_id(username)
    +        circles = self.api.projects.list(member=user_id)
    +        user_circles = []
    +        for circle in circles:
    +            if circle.owner["id"] == user_id:
    +                user_circles.append(self._resolve_object(circle))
    +        return user_circles
    +
    +    def get_circles_issues(self, project_id):
    +        """Get all issues in a circle/project
    +
    +        Args:
    +            project_id (int): id of the circle/project
    +
    +        Raises:
    +            j.exceptions.NotFound: if couldn't find circle with specified id
    +        """
    +        try:
    +            circle = self.api.projects.get(project_id)
    +        except TaigaRestException:
    +            raise j.exceptions.NotFound(f"Couldn't find project with id: {project_id}")
    +
    +        circle_issues = []
    +        for issue in circle.list_issues():
    +            issue.project = self._get_project(issue.project)
    +            issue.milestone = self._get_milestone(issue.milestone)
    +            issue.priority = self._get_priority(issue.priority)
    +            issue.assignee = self._get_assignee(issue.assigned_to)
    +            issue.status = self._get_issue_status(issue.status)
    +            circle_issues.append(issue)
    +        return circle_issues
    +
    +    def get_user_stories(self, username):
    +        """Get all stories of a user
    +
    +        Args:
    +            username (str): Name of the user
    +        """
    +        user_id = self._get_user_id(username)
    +        user_stories = self.api.user_stories.list(assigned_to=user_id)
    +        user_stories = []
    +        for user_story in user_stories:
    +            # user_story.project = self._get_project(user_story.project)
    +            # user_story.milestone = self._get_milestone(user_story.milestone)
    +            user_story.status = self._get_user_stories_status(user_story.status)
    +            user_stories.append(user_story)
    +        return user_stories
    +
    +    def get_user_tasks(self, username):
    +        """Get all tasks of a user
    +
    +        Args:
    +            username (str): Name of the user
    +        """
    +        user_id = self._get_user_id(username)
    +        user_tasks = self.api.tasks.list(assigned_to=user_id)
    +        user_tasks = []
    +        for user_task in user_tasks:
    +            # user_task.project = self._get_project(user_task.project)
    +            # user_task.milestone = self._get_milestone(user_task.milestone)
    +            user_task.status = self._get_task_status(user_task.status)
    +            user_tasks.append(self._resolve_object(user_task))
    +
    +        return user_tasks
    +
    +    def move_story_to_circle(self, story_id, project_id):
    +        """Moves a story to another circle/project
    +
    +        Args:
    +            story_id (int): User story id
    +            project_id (int): circle/project id
    +
    +        Raises:
    +            j.exceptions.NotFound: No user story with specified id found
    +            j.exceptions.NotFound: No project with specified id found
    +            j.exceptions.Runtime: [description]
    +
    +        Returns:
    +            int: New id of the migrated user story
    +        """
    +
    +        def _get_project_status(project_statuses, status):
    +            for project_status in project_statuses:
    +                if project_status.name == status:
    +                    return project_status.id
    +
    +        try:
    +            user_story = self.api.user_stories.get(story_id)
    +        except TaigaRestException:
    +            raise j.exceptions.NotFound(f"Couldn't find user story with id: {story_id}")
    +
    +        project_stories_statuses = self.api.user_story_statuses.list(project=project_id)
    +        status = self._get_user_stories_status(user_story.status)
    +        story_status_id = _get_project_status(project_stories_statuses, status)
    +
    +        try:
    +            migrate_story = self.api.user_stories.create(
    +                project=project_id,
    +                subject=user_story.subject,
    +                assigned_to=user_story.assigned_to,
    +                milestone=user_story.milestone,
    +                status=story_status_id,
    +                tags=user_story.tags,
    +            )
    +        except TaigaRestException:
    +            raise j.exceptions.NotFound(f"No project with id: {project_id} found")
    +        try:
    +            comments = self.api.history.user_story.get(story_id)
    +            comments = sorted(comments, key=lambda c: dateutil.parser.isoparse(c["created_at"]))
    +
    +            for comment in comments:
    +                migrate_story.add_comment(comment["comment_html"])
    +
    +            project_tasks_statuses = self.api.task_statuses.list(project=project_id)
    +            for task in user_story.list_tasks():
    +                status = self._get_task_status(task.status)
    +                task_status_id = _get_project_status(project_tasks_statuses, status)
    +                migrate_task = migrate_story.add_task(
    +                    subject=task.subject,
    +                    status=task_status_id,
    +                    due_date=task.due_date,
    +                    milestone=task.milestone,
    +                    assigned_to=task.assigned_to,
    +                    tags=task.tags,
    +                    project=migrate_story.project,
    +                    user_story=migrate_story.id,
    +                )
    +                comments = self.api.history.task.get(migrate_task.id)
    +                comments = sorted(comments, key=lambda c: dateutil.parser.isoparse(c["created_at"]))
    +
    +                for comment in comments:
    +                    migrate_task.add_comment(comment["comment_html"])
    +
    +        except Exception as e:
    +            self.api.user_stories.delete(migrate_story.id)
    +            raise j.exceptions.Runtime(f"Failed to migrate story error was: {str(e)}")
    +
    +        self.api.user_stories.delete(story_id)
    +        return migrate_story.id
    +
    +    def list_all_issues(self, username="", full_info=False):
    +        """
    +        List all issues for specific user if you didn't pass user_id will list all the issues
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            username (str): username.
    +            full_info (bool): flag used to get object with full info. Defaults to False.
    +
    +        Returns:
    +            List: List of taiga.models.models.Issue.
    +        """
    +        if username:
    +            user_id = self._get_user_id(username)
    +            if not full_info:
    +                return [CircleIssue(self, self._resolve_object(x)) for x in self.api.issues.list(assigned_to=user_id)]
    +            else:
    +                return [CircleIssue(self, self.api.issues.get(x.id)) for x in self.api.issues.list(assigned_to=user_id)]
    +        else:
    +            if not full_info:
    +                return [CircleIssue(self, self._resolve_object(x)) for x in self.api.issues.list()]
    +            else:
    +                return [CircleIssue(self, self.api.issues.get(x.id)) for x in self.api.issues.list()]
    +
    +    def list_all_tasks(self, username="", full_info=False):
    +        """
    +        List all tasks for specific user if you didn't pass user_id will list all the tasks
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            username (str): username.
    +            full_info (bool): flag used to get object with full info. Defaults to False.
    +
    +        Returns:
    +            List: List of taiga.models.models.Task.
    +        """
    +        if username:
    +            user_id = self._get_user_id(username)
    +            if not full_info:
    +                return [CircleTask(self, self._resolve_object(x)) for x in self.api.tasks.list(assigned_to=user_id)]
    +            else:
    +                return [CircleTask(self, self.api.tasks.get(x.id)) for x in self.api.tasks.list(assigned_to=user_id)]
    +        else:
    +            if not full_info:
    +                return [CircleTask(self, self._resolve_object(x)) for x in self.api.tasks.list()]
    +            else:
    +                return [CircleTask(self, self.api.tasks.get(x.id)) for x in self.api.tasks.list()]
    +
    +    def list_all_projects(self, full_info=False):
    +        """
    +        List all projects
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            full_info(bool): flag used to get object with full info. Defaults to False.
    +
    +        Returns:
    +            List: List of taiga.models.models.Project.
    +        """
    +        if not full_info:
    +            return [Circle(self, self._resolve_object(x)) for x in self.api.projects.list()]
    +        else:
    +            return [Circle(self, self.api.projects.get(x.id)) for x in self.api.projects.list()]
    +
    +    def list_all_active_projects(self, full_info=False):
    +        """
    +        List all projects not starting with "ARHCIVE"
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            full_info (bool): [description]. Defaults to False.
    +
    +        Returns:
    +            [type]: [description]
    +        """
    +        return [
    +            Circle(self, p)
    +            for p in self.list_projects_by(lambda x: not x.name.startswith("ARCHIVE_"), full_info=full_info)
    +        ]
    +
    +    def list_all_milestones(self):
    +        """
    +        List all milestones
    +
    +        Returns:
    +            List: List of taiga.models.models.Milestone.
    +        """
    +        return [self._resolve_object(x) for x in self.api.milestones.list()]
    +
    +    def list_all_user_stories(self, username="", full_info=False):
    +        """
    +        List all user stories for specific user if you didn't pass user_id will list all the available user stories
    +        HINT: Using full_info will take a longer time
    +
    +        Args:
    +            username (str): username.
    +            full_info(bool): flag used to get object with full info. Defaults to False
    +
    +        Returns:
    +            List: List of CircleStory.
    +        """
    +        if username:
    +            user_id = self._get_user_id(username)
    +            if not full_info:
    +                return [
    +                    CircleStory(self, self._resolve_object(x)) for x in self.api.user_stories.list(assigned_to=user_id)
    +                ]
    +            else:
    +                return [
    +                    CircleStory(self, self.api.user_stories.get(x.id))
    +                    for x in self.api.user_stories.list(assigned_to=user_id)
    +                ]
    +        else:
    +            if not full_info:
    +                return [CircleStory(self, self._resolve_object(x)) for x in self.api.user_stories.list()]
    +            else:
    +                return [CircleStory(self, self.api.user_stories.get(x.id)) for x in self.api.user_stories.list()]
    +
    +    def list_all_users(self, full_info=False):
    +        """
    +        List all user stories for specific user if you didn't pass user_id will list all the available user stories
    +        HINT: Using full_info will take a longer time
    +        Args:
    +            username (str): username.
    +            full_info(bool): flag used to get object with full info. Defaults to False
    +
    +        Returns:
    +            List: List of CircleUser.
    +        """
    +        circles = self.list_all_projects()
    +        users = set()
    +        for c in circles:
    +            for m in c.members:
    +                users.add(m)
    +
    +        return [CircleUser(self, self._get_user_by_id(uid)) for uid in users]
    +
    +    def get_issue_by_id(self, issue_id):
    +        """Get issue
    +        Args:
    +            issue_id: the id of the desired issue
    +
    +        Returns:
    +            Issue object: issue
    +        """
    +        return CircleIssue(self, self.api.issues.get(issue_id))
    +
    +    def _resolve_object(self, obj):
    +        resolvers = {
    +            "owners": self._get_users_by_ids,
    +            "watchers": self._get_users_by_ids,
    +            "members": self._get_users_by_ids,
    +            "project": self._get_project,
    +            "circle": self._get_project,
    +            "milestone": self._get_milestone,
    +            "task_status": self._get_task_status,
    +            "assigned_to": self._get_user_by_id,
    +            "owner": self._get_user_by_id,
    +            "issues": self._get_issues_by_ids,
    +            "tasks": self._get_task_by_id,
    +        }
    +        newobj = copy.deepcopy(obj)
    +        for k in dir(newobj):
    +            v = getattr(newobj, k)
    +            if isinstance(v, int) or isinstance(v, list) and v and isinstance(v[0], int):
    +                if k in resolvers:
    +                    resolved = None
    +                    resolver = resolvers[k]
    +                    try:
    +                        copied_v = copy.deepcopy(v)
    +                        resolved = lambda: resolver(copied_v)
    +
    +                        if isinstance(v, list):
    +                            setattr(newobj, f"{k}_objects", resolved)
    +                        else:
    +                            setattr(newobj, f"{k}_object", resolved)
    +
    +                    except Exception as e:
    +                        import traceback
    +
    +                        traceback.print_exc()
    +
    +                        j.logger.error(f"error {e}")
    +
    +        return newobj
    +
    +    def list_projects_by(self, fn=lambda x: True, full_info=False):
    +        return [p for p in self.list_all_projects(full_info=full_info) if fn(p)]
    +
    +    def list_team_circles(self):
    +        return [TeamCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("TEAM_"))]
    +
    +    def list_project_circles(self):
    +        return [ProjectCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("PROJECT_"))]
    +
    +    def list_funnel_circles(self):
    +        return [FunnelCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("FUNNEL_"))]
    +
    +    def validate_custom_fields(self, attributes):
    +        """Validate custom fields values to match our requirements
    +
    +        Args:
    +            attributes (List): Output from get_issue/story_custom_fields functions
    +
    +        Raises:
    +            j.exceptions.Validation: Raise validation exception if any input not valid
    +
    +        Returns:
    +            bool: Return True if no exception raised and print logs
    +        """
    +
    +        for attr in attributes:
    +            name = attr.get("name")
    +            value = attr.get("value")
    +
    +            period = value.get("period", "onetime")
    +            duration = value.get("duration", 1)
    +            amount = value.get("amount", 0)
    +            currency = value.get("currency", "eur")
    +            start_date = value.get("start_date", f"{dateutil.utils.today().month}:{dateutil.utils.today().year}",)
    +            confidence = value.get("confidence", 100)
    +            user = value.get("user")
    +            part = value.get("part", "0%")
    +            type = value.get("type", "revenue")
    +
    +            if name not in ["bookings", "commission"]:
    +                raise j.exceptions.Validation(
    +                    f'Name: ({name}) is unknown custom field, please select one of the following ["bookings", "commission"]'
    +                )
    +
    +            if period not in ["onetime", "month", "year"]:
    +                raise j.exceptions.Validation(
    +                    f'Period: ({period}) not found, please select one of following ["onetime", "month", "year"]'
    +                )
    +
    +            if duration < 1 or duration > 120:
    +                raise j.exceptions.Validation(f"Duration: ({duration}) is not in range, please select it from 1 to 120")
    +
    +            if not isinstance(amount, int):
    +                raise j.exceptions.Validation(f"Amount: ({amount}) is not integer, please add int value")
    +
    +            if currency.replace(" ", "").lower() not in [
    +                "usd",
    +                "chf",
    +                "eur",
    +                "gbp",
    +                "egp",
    +            ]:
    +                raise j.exceptions.Validation(
    +                    f'Currency: ({currency}) is not supported, please use one of the following currencies ["usd", "chf", "eur", "gbp", "egp"]'
    +                )
    +            try:
    +                date = start_date.split(":")
    +                month = int(date[0])
    +                year = int(date[1]) if len(date) > 1 else dateutil.utils.today().year
    +                if month < 1 or month > 12:
    +                    raise j.exceptions.Validation(
    +                        "Please use values from 1 to 12 in Month field, follow format like MONTH:YEAR as 11:2020 or MONTH as 11"
    +                    )
    +            except ValueError as e:
    +                raise j.exceptions.Validation(
    +                    "Please use numeric date with the following format MONTH:YEAR as 11:2020 or MONTH as 11"
    +                )
    +            except AttributeError as e:
    +                pass  # Will check what happen if start_date not provide
    +
    +            if confidence % 10 != 0:
    +                j.exceptions.Validation(f"Confidence: ({confidence}) not multiple of 10, it must be multiple of 10")
    +
    +            part_tmp = part.replace("%", "")
    +            if user != None and user not in self.list_all_users():
    +                raise j.exceptions.Validation(f"User: ({user}) is not found")
    +
    +            if int(part_tmp) < 0 or int(part_tmp) > 100:
    +                j.exceptions.Validation(f"Part: ({part}) is a not a valid percentage, it must be from 0% to 100%")
    +
    +            if type not in ["revenue", "booking"]:
    +                raise j.exceptions.Validation(
    +                    f'Type: ({type}) is not supported type, please choose one of the following ["revenue" , "booking"]'
    +                )
    +
    +            j.logger.info(f"Attribute: {name} passed")
    +
    +        return True
    +
    +    def _create_new_circle(
    +        self,
    +        name,
    +        type_="team",
    +        description="desc",
    +        severities=None,
    +        issues_statuses=None,
    +        priorities=None,
    +        issues_types=None,
    +        user_stories_statuses=None,
    +        tasks_statuses=None,
    +        custom_fields=None,
    +        **attrs,
    +    ):
    +        severities = severities or ["Low", "Mid", "High"]
    +        priorities = priorities or [
    +            "Wishlist",
    +            "Minor",
    +            "Normal",
    +            "Important",
    +            "Critical",
    +        ]
    +        issues_statuses = issues_statuses or [
    +            "New",
    +            "In progress",
    +            "Ready for test",
    +            "Closed",
    +            "Needs Info",
    +            "Rejected",
    +            "Postponed",
    +        ]
    +        issues_types = issues_types or []
    +        user_stories_statuses = user_stories_statuses or []
    +        tasks_statuses = tasks_statuses or []
    +        custom_fields = custom_fields or []
    +
    +        type_ = type_.upper()
    +        project_name = f"{type_}_{name}"
    +        p = self.api.projects.create(project_name, description=description)
    +        for t in tasks_statuses:
    +            try:
    +                p.add_task_status(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping task {t} {e}")
    +
    +        for t in priorities:
    +            try:
    +                p.add_priority(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping prio {t} {e}")
    +
    +        for t in severities:
    +            try:
    +                p.add_severity(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping sever {t} {e}")
    +
    +        for t in issues_statuses:
    +            try:
    +                p.add_issue_status(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping status {t} {e}")
    +
    +        for t in user_stories_statuses:
    +            try:
    +                p.add_user_story_status(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping user status {t} {e}")
    +
    +        for t in issues_types:
    +            try:
    +                p.add_issue_type(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping issue type {t} {e}")
    +
    +        for t in custom_fields:
    +            try:
    +                p.add_issue_attribute(t)
    +                p.add_user_story_attribute(t)
    +            except Exception as e:
    +                # check if duplicated
    +                j.logger.debug(f"skipping custom field type {t} {e}")
    +
    +        return p
    +
    +    def create_new_project_circle(
    +        self, name, description="", **attrs,
    +    ):
    +        """Creates a new project circle.
    +
    +        Args:
    +            name (str): circle name
    +            description (str, optional): circle description. Defaults to "".
    +
    +        Returns:
    +            [ProjectCircle]: Project circle
    +        """
    +
    +        attrs = {
    +            "is_backlog_activated": False,
    +            "is_issues_activated": True,
    +            "is_kanban_activated": True,
    +            "is_private": False,
    +            "is_wiki_activated": True,
    +        }
    +        issues_types = ["Bug", "Question", "Enhancement"]
    +        severities = ["Wishlist", "Minor", "Normal", "Important", "Critical"]
    +        priorities = None
    +
    +        story_statuses = [
    +            "New",
    +            "to-start",
    +            "in-progress",
    +            "Blocked",
    +            "Implemented",
    +            "Verified",
    +            "Archived",
    +        ]
    +        item_statuses = ["New", "to-start", "in-progress", "Blocked", "Done"]
    +        issues_statuses = [
    +            "New",
    +            "to-start",
    +            "in-progress",
    +            "Blocked",
    +            "Implemented",
    +            "Closed",
    +            "Rejected",
    +            "Postponed",
    +            "Archived",
    +        ]
    +
    +        return ProjectCircle(
    +            self,
    +            self._create_new_circle(
    +                name,
    +                type_="project",
    +                description=description,
    +                severities=severities,
    +                issues_statuses=issues_statuses,
    +                priorities=priorities,
    +                issues_types=issues_types,
    +                user_stories_statuses=story_statuses,
    +                tasks_statuses=item_statuses,
    +                **attrs,
    +            ),
    +        )
    +
    +    def create_new_team_circle(self, name, description="", **attrs):
    +        """Creates a new team circle. using sprints & timeline (does not use kanban)
    +
    +        Args:
    +            name (str): circle name
    +            description (str, optional): circle description. Defaults to "".
    +            severities (List[str], optional): list of strings to represent severities. Defaults to None.
    +            issues_statuses (List[str], optional): list of strings to represent issues_stauses. Defaults to None.
    +            priorities (List[str], optional): list of strings to represent priorities. Defaults to None.
    +            issues_types (List[str], optional): list of strings to represent issues types. Defaults to None.
    +            user_stories_statuses (List[str], optional): list of strings to represent user stories. Defaults to None.
    +            tasks_statuses (List[str], optional): list of strings to represent task statuses. Defaults to None.
    +
    +        Returns:
    +            [TeamCircle]: team circle
    +        """
    +
    +        attrs = {
    +            "is_backlog_activated": True,
    +            "is_issues_activated": True,
    +            "is_kanban_activated": False,
    +            "is_private": False,
    +            "is_wiki_activated": True,
    +        }
    +        issues_types = ["Bug", "Question", "Enhancement"]
    +        severities = ["Wishlist", "Minor", "Normal", "Important", "Critical"]
    +        priorities = None
    +
    +        story_statuses = [
    +            "New",
    +            "to-start",
    +            "in-progress",
    +            "Blocked",
    +            "Implemented",
    +            "Verified",
    +            "Archived",
    +        ]
    +        item_statuses = ["New", "to-start", "in-progress", "Blocked", "Done"]
    +        issues_statuses = [
    +            "New",
    +            "to-start",
    +            "in-progress",
    +            "Blocked",
    +            "Implemented",
    +            "Closed",
    +            "Rejected",
    +            "Postponed",
    +            "Archived",
    +        ]
    +
    +        return TeamCircle(
    +            self,
    +            self._create_new_circle(
    +                name,
    +                type_="team",
    +                description=description,
    +                severities=severities,
    +                issues_statuses=issues_statuses,
    +                priorities=priorities,
    +                issues_types=issues_types,
    +                user_stories_statuses=story_statuses,
    +                tasks_statuses=item_statuses,
    +                **attrs,
    +            ),
    +        )
    +
    +    def create_new_funnel_circle(self, name, description="", **attrs):
    +        """Creates a new funnel circle. using sprints & timeline (does not use kanban)
    +
    +        Args:
    +            name (str): circle name
    +            description (str, optional): circle description. Defaults to "".
    +
    +        Returns:
    +            [FunnelCircle]: funnel circle
    +        """
    +        attrs = {
    +            "is_backlog_activated": False,
    +            "is_issues_activated": True,
    +            "is_kanban_activated": True,
    +            "is_private": False,
    +            "is_wiki_activated": True,
    +        }
    +
    +        severities = ["unknown", "low", "25%", "50%", "75%", "90%"]
    +        priorities = ["Low", "Normal", "High"]
    +        issues_types = "opportunity"
    +
    +        issues_statuses = [
    +            "New",
    +            "Interested",
    +            "Deal",
    +            "Blocked",
    +            "NeedInfo",
    +            "Lost",
    +            "Postponed",
    +            "Won",
    +        ]
    +        story_statuses = [
    +            "New",
    +            "Proposal",
    +            "Contract",
    +            "Blocked",
    +            "NeedInfo",
    +            "Closed",
    +        ]
    +        task_statuses = ["New", "In progress", "Verification", "Needs info", "Closed"]
    +
    +        custom_fields = ["bookings", "commission"]
    +
    +        return FunnelCircle(
    +            self,
    +            self._create_new_circle(
    +                name,
    +                type_="funnel",
    +                description=description,
    +                severities=severities,
    +                issues_statuses=issues_statuses,
    +                priorities=priorities,
    +                issues_types=issues_types,
    +                user_stories_statuses=story_statuses,
    +                tasks_statuses=task_statuses,
    +                custom_fields=custom_fields,
    +                **attrs,
    +            ),
    +        )
    +
    +    def export_circles_as_md(self, wikipath="/tmp/taigawiki", modified_only=True, full_info=False):
    +        """export circles into {wikipath}/src/circles
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            wikipath (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +            full_info (bool): export object with full info. Defaults to False
    +        """
    +        path = j.sals.fs.join_paths(wikipath, "src", "circles")
    +
    +        j.sals.fs.mkdirs(path)
    +        circles = self.list_all_active_projects(full_info=full_info)
    +
    +        def write_md_for_circle(circle):
    +            circle_md = circle.as_md
    +            circle_mdpath = j.sals.fs.join_paths(path, f"{circle.clean_name}.md")
    +            if not (
    +                modified_only and j.sals.fs.exists(circle_mdpath) and j.sals.fs.read_ascii(circle_mdpath) == circle_md
    +            ):
    +                j.sals.fs.write_ascii(circle_mdpath, circle_md)
    +
    +        circles_mdpath = j.sals.fs.join_paths(path, "circles.md")
    +        circles_mdcontent = "# circles\n\n"
    +        for c in circles:
    +            circles_mdcontent += f"- [{c.name}](./{c.clean_name}.md)\n"
    +
    +        j.sals.fs.write_ascii(circles_mdpath, circles_mdcontent)
    +
    +        greenlets = [gevent.spawn(write_md_for_circle, gcircle_obj) for gcircle_obj in circles]
    +        gevent.joinall(greenlets)
    +
    +    def export_users_as_md(self, wikipath="/tmp/taigawiki", modified_only=True, full_info=False):
    +        """export users into {wikipath}/src/users
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            wikipath (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +            modified_only (bool): export moidified objects only
    +            full_info (bool): export object with full info. Defaults to False
    +        """
    +
    +        path = j.sals.fs.join_paths(wikipath, "src", "users")
    +        j.sals.fs.mkdirs(path)
    +        users_objects = self.list_all_users(full_info=full_info)
    +
    +        users_mdpath = j.sals.fs.join_paths(path, "users.md")
    +        users_mdcontent = "# users\n\n"
    +
    +        def write_md_for_user(user):
    +            user_md = user.as_md
    +            user_mdpath = j.sals.fs.join_paths(path, f"{user.clean_name}.md")
    +            if not (modified_only and j.sals.fs.exists(user_mdpath) and j.sals.fs.read_ascii(user_mdpath) == user_md):
    +                j.sals.fs.write_ascii(user_mdpath, user_md)
    +
    +        for u in users_objects:
    +            users_mdcontent += f"- [{u.username}](./{u.clean_name}.md)\n"
    +
    +        j.sals.fs.write_ascii(users_mdpath, users_mdcontent)
    +
    +        greenlets = [gevent.spawn(write_md_for_user, guser_obj) for guser_obj in users_objects]
    +        gevent.joinall(greenlets)
    +
    +    def export_as_md(self, wiki_path="/tmp/taigawiki", modified_only: bool = True, full_info=False):
    +        """export taiga instance into a wiki  showing users and circles
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            wiki_src_path (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +            modified_only (bool): write modified objects only. Defaults to True
    +            full_info (bool): export object with full info. Defaults to False
    +        """
    +        j.logger.info("Start Exporting Wiki ...")
    +        gs = []
    +        gs.append(gevent.spawn(self.export_circles_as_md, wiki_path, modified_only, full_info))
    +        gs.append(gevent.spawn(self.export_users_as_md, wiki_path, modified_only, full_info))
    +        gevent.joinall(gs)
    +
    +        template_file = j.sals.fs.join_paths(Path(__file__).parent, "template.html")
    +        index_html_path = j.sals.fs.join_paths(wiki_path, "src", "index.html")
    +        readme_md_path = j.sals.fs.join_paths(wiki_path, "src", "README.md")
    +        sidebar_md_path = j.sals.fs.join_paths(wiki_path, "src", "_sidebar.md")
    +        content = dedent(
    +            f"""
    +            # Taiga overview
    +
    +            - [circles](./circles/circles.md)
    +            - [users](./users/users.md)
    +        """
    +        )
    +        j.sals.fs.write_ascii(readme_md_path, content)
    +        j.sals.fs.write_ascii(sidebar_md_path, content)
    +        j.sals.fs.copy_file(template_file, index_html_path)
    +        j.logger.info(f"Exported at {wiki_path}")
    +
    +    def export_as_md_periodically(
    +        self, wiki_path="/tmp/taigawiki", period: int = 300, modified_only: bool = True, full_info=False
    +    ):
    +        """export taiga instance into a wiki  showing users and circles periodically
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            wiki_path (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +            period (int): Time to wait between each export in "Seconds". Defaults to 300 (5 Min).
    +            modified_only (bool): write modified objects only.. Defaults to True.
    +            full_info (bool): export object with full info. Defaults to False
    +
    +        """
    +        repeater = Event()
    +        while True:
    +            j.logger.info("Start Exporting ....")
    +            self.export_as_md(wiki_path, modified_only, full_info)
    +            j.logger.info(f"Exported at {wiki_path}")
    +            repeater.wait(period)
    +
    +    def export_as_yaml(self, export_dir="/tmp/export_dir", full_info=False):
    +        """export taiga instance [Circle, Story, Issue, Task , User] into a yaml files
    +        HINT: Using full_info will take longer time
    +
    +        Args:
    +            export_dir (str, optional): [description]. Defaults to "/tmp/export_dir".
    +            full_info (bool): export object with full info. Defaults to False
    +        """
    +
    +        def _export_objects_to_dir(objects_dir, objects_fun, full_info):
    +            j.sals.fs.mkdirs(objects_dir)
    +            objects = objects_fun(full_info=full_info)
    +            for obj in objects:
    +                try:
    +                    outpath = j.sals.fs.join_paths(objects_dir, f"{obj.id}.yaml")
    +                    j.sals.fs.write_ascii(outpath, obj.as_yaml)
    +
    +                except Exception as e:
    +                    import traceback
    +
    +                    traceback.print_exc()
    +                    j.logger.error(e)
    +                    j.logger.error(f"{type(obj)}: {obj.id}")
    +
    +        projects_path = j.sals.fs.join_paths(export_dir, "projects")
    +        stories_path = j.sals.fs.join_paths(export_dir, "stories")
    +        issues_path = j.sals.fs.join_paths(export_dir, "issues")
    +        tasks_path = j.sals.fs.join_paths(export_dir, "tasks")
    +        # Milestones is not one of our model objects
    +        # milestones_path = j.sals.fs.join_paths(export_dir, "milestones")
    +        users_path = j.sals.fs.join_paths(export_dir, "users")
    +
    +        def on_err(*args, **kwargs):
    +            print("err, ", args, kwargs)
    +
    +        j.logger.info("Start Export as YAML")
    +
    +        gs = []
    +        gs.append(gevent.spawn(_export_objects_to_dir, projects_path, self.list_all_active_projects, full_info))
    +        gs.append(gevent.spawn(_export_objects_to_dir, stories_path, self.list_all_user_stories, full_info))
    +        gs.append(gevent.spawn(_export_objects_to_dir, issues_path, self.list_all_issues, full_info))
    +        # Milestones is not one of our model objects
    +        # gs.append(gevent.spawn(_export_objects_to_dir, milestones_path, self.list_all_milestones)
    +        gs.append(gevent.spawn(_export_objects_to_dir, users_path, self.list_all_users, full_info))
    +        gs.append(gevent.spawn(_export_objects_to_dir, tasks_path, self.list_all_tasks, full_info))
    +        gevent.joinall(gs)
    +
    +        j.logger.info("Finish Export as YAML")
    +
    +    def import_from_yaml(self, import_dir="/tmp/export_dir"):
    +        """Import Circle with all stories, issues and tasks from yaml files
    +
    +        Args:
    +            import_dir (str): import directory path. Defaults to "/tmp/export_dir".
    +        """
    +        # Helper Functions
    +        def check_by_name(list_obj, name_to_find):
    +            for obj in list_obj:
    +                if obj.name == name_to_find:
    +                    return obj.id
    +
    +            return list_obj[0].id
    +
    +        def import_circle(self, yaml_obj):
    +            circle = None
    +            # Funnel Circle
    +            if yaml_obj["basic_info"]["name"].lower() == "funnel":
    +                circle = self.create_new_funnel_circle(
    +                    yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +                )
    +            # Team Circle
    +            elif yaml_obj["basic_info"]["name"].lower() == "team":
    +                circle = self.create_new_team_circle(
    +                    yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +                )
    +            # Project Circle
    +            elif yaml_obj["basic_info"]["name"].lower() == "project":
    +                circle = self.create_new_project_circle(
    +                    yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +                )
    +            # Any Other Circle
    +            else:
    +                circle = self._create_new_circle(yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],)
    +                circle.is_backlog_activated = yaml_obj["modules"]["is_backlog_activated"]
    +                circle.is_issues_activated = yaml_obj["modules"]["is_issues_activated"]
    +                circle.is_kanban_activated = yaml_obj["modules"]["is_kanban_activated"]
    +                circle.is_wiki_activated = yaml_obj["modules"]["is_wiki_activated"]
    +
    +            circle.is_private = yaml_obj["basic_info"]["is_private"]
    +            circle.videoconferences = yaml_obj["modules"]["videoconferences"]
    +            for issue_attr in yaml_obj["issues_attributes"]:
    +                circle.add_issue_attribute(issue_attr)
    +            for us_attr in yaml_obj["stories_attributes"]:
    +                circle.add_user_story_attribute(us_attr)
    +            return circle
    +
    +        def import_story(circle_object, yaml_obj):
    +            status_id = check_by_name(circle_object.us_statuses, yaml_obj["status"]["name"])
    +
    +            story = circle_object.add_user_story(
    +                yaml_obj["basic_info"].get("subject"),
    +                tags=yaml_obj["basic_info"].get("tags"),
    +                description=yaml_obj["basic_info"].get("description", ""),
    +                client_requirement=yaml_obj["requirements"].get("client_requirement"),
    +                team_requirement=yaml_obj["requirements"].get("team_requirement"),
    +                is_blocked=yaml_obj["additional_info"].get("is_blocked"),
    +                due_date=yaml_obj["date"].get("due_date"),
    +                status=status_id,
    +            )
    +
    +            for field in yaml_obj.get("custom_fields", []):
    +                for attr in circle_object.list_user_story_attributes():
    +                    if attr.name == field["name"]:
    +                        story.set_attribute(attr.id, field["value"])
    +                        break
    +            return story
    +
    +        def import_issue(circle_object, yaml_obj):
    +            priority_id = check_by_name(circle_object.priorities, yaml_obj["priority"]["name"])
    +            status_id = check_by_name(circle_object.issue_statuses, yaml_obj["status"]["name"])
    +            type_id = check_by_name(circle_object.issue_types, yaml_obj["type"]["name"])
    +            severity_id = check_by_name(circle_object.severities, yaml_obj["severity"]["name"])
    +            issue = circle_object.add_issue(
    +                yaml_obj["basic_info"]["subject"],
    +                priority_id,
    +                status_id,
    +                type_id,
    +                severity_id,
    +                description=yaml_obj["basic_info"].get("description"),
    +            )
    +
    +            for field in yaml_obj["custom_fields"]:
    +                for attr in circle_object.list_issue_attributes():
    +                    if attr.name == field["name"]:
    +                        issue.set_attribute(attr.id, field["value"])
    +                        break
    +            return issue
    +
    +        def import_tasks(circle_object, story_object, yaml_obj):
    +            status_id = check_by_name(circle_object.task_statuses, yaml_obj["status"]["name"])
    +            task = story_object.add_task(
    +                yaml_obj["basic_info"].get("subject"),
    +                status_id,
    +                description=yaml_obj["basic_info"].get("description", ""),
    +                tags=yaml_obj["basic_info"].get("tags"),
    +            )
    +            return task
    +
    +        # Folders Path
    +        projects_path = j.sals.fs.join_paths(import_dir, "projects")
    +        stories_path = j.sals.fs.join_paths(import_dir, "stories")
    +        issues_path = j.sals.fs.join_paths(import_dir, "issues")
    +        tasks_path = j.sals.fs.join_paths(import_dir, "tasks")
    +
    +        # List of Files inside project Folder
    +        projects = j.sals.fs.os.listdir(projects_path)
    +
    +        for project_file in projects:
    +            if project_file.endswith(".yaml") or project_file.endswith(".yml"):
    +                with open(j.sals.fs.join_paths(projects_path, project_file)) as pf:
    +                    circle_yaml = yaml.full_load(pf)
    +                    circle_obj = import_circle(self, circle_yaml)
    +                    j.logger.info(f"<Circle {circle_obj.id} Created>")
    +
    +                    for story in circle_yaml["stories"]:
    +                        with open(j.sals.fs.join_paths(stories_path, f"{story}.yaml")) as sf:
    +                            story_yaml = yaml.full_load(sf)
    +                            story_obj = import_story(circle_obj, story_yaml)
    +                            j.logger.info(f"<Story {story_obj.id} Created in Circle {circle_obj.id}>")
    +
    +                            for task in story_yaml["tasks"]:
    +                                with open(j.sals.fs.join_paths(tasks_path, f"{task}.yaml")) as tf:
    +                                    task_yaml = yaml.full_load(tf)
    +                                    task_obj = import_tasks(circle_obj, story_obj, task_yaml)
    +                                    j.logger.info(f"<Task {task_obj.id} Created in Story {story_obj.id}>")
    +                                    task_obj.update()
    +                            story_obj.update()
    +
    +                    for issue in circle_yaml["issues"]:
    +                        with open(j.sals.fs.join_paths(issues_path, f"{issue}.yaml")) as isf:
    +                            issue_yaml = yaml.full_load(isf)
    +                            issue_obj = import_issue(circle_obj, issue_yaml)
    +                            j.logger.info(f"<Issue {issue_obj.id} Created in Circle {circle_obj.id}>")
    +                            issue_obj.update()
    +
    +                    circle_obj.update()
    +                    j.logger.info(f"<Circle {circle_obj.id} Imported with All Stories and Issues")
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var api
    +
    +
    +
    + +Expand source code + +
    @property
    +def api(self):
    +    if not self._api:
    +        api = TaigaAPI(host=self.host)
    +        if self.token:
    +            api.token = self.token
    +        else:
    +            if not self.username or not self.password:
    +                raise j.exceptions.Runtime("Token or username and password are required")
    +            api.auth(self.username, self.password)
    +        self._api = api
    +    return self._api
    +
    +
    +
    var host
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var password
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var token
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var username
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def create_new_funnel_circle(self, name, description='', **attrs) +
    +
    +

    Creates a new funnel circle. using sprints & timeline (does not use kanban)

    +

    Args

    +
    +
    name : str
    +
    circle name
    +
    description : str, optional
    +
    circle description. Defaults to "".
    +
    +

    Returns

    +
    +
    [FunnelCircle]
    +
    funnel circle
    +
    +
    + +Expand source code + +
    def create_new_funnel_circle(self, name, description="", **attrs):
    +    """Creates a new funnel circle. using sprints & timeline (does not use kanban)
    +
    +    Args:
    +        name (str): circle name
    +        description (str, optional): circle description. Defaults to "".
    +
    +    Returns:
    +        [FunnelCircle]: funnel circle
    +    """
    +    attrs = {
    +        "is_backlog_activated": False,
    +        "is_issues_activated": True,
    +        "is_kanban_activated": True,
    +        "is_private": False,
    +        "is_wiki_activated": True,
    +    }
    +
    +    severities = ["unknown", "low", "25%", "50%", "75%", "90%"]
    +    priorities = ["Low", "Normal", "High"]
    +    issues_types = "opportunity"
    +
    +    issues_statuses = [
    +        "New",
    +        "Interested",
    +        "Deal",
    +        "Blocked",
    +        "NeedInfo",
    +        "Lost",
    +        "Postponed",
    +        "Won",
    +    ]
    +    story_statuses = [
    +        "New",
    +        "Proposal",
    +        "Contract",
    +        "Blocked",
    +        "NeedInfo",
    +        "Closed",
    +    ]
    +    task_statuses = ["New", "In progress", "Verification", "Needs info", "Closed"]
    +
    +    custom_fields = ["bookings", "commission"]
    +
    +    return FunnelCircle(
    +        self,
    +        self._create_new_circle(
    +            name,
    +            type_="funnel",
    +            description=description,
    +            severities=severities,
    +            issues_statuses=issues_statuses,
    +            priorities=priorities,
    +            issues_types=issues_types,
    +            user_stories_statuses=story_statuses,
    +            tasks_statuses=task_statuses,
    +            custom_fields=custom_fields,
    +            **attrs,
    +        ),
    +    )
    +
    +
    +
    +def create_new_project_circle(self, name, description='', **attrs) +
    +
    +

    Creates a new project circle.

    +

    Args

    +
    +
    name : str
    +
    circle name
    +
    description : str, optional
    +
    circle description. Defaults to "".
    +
    +

    Returns

    +
    +
    [ProjectCircle]
    +
    Project circle
    +
    +
    + +Expand source code + +
    def create_new_project_circle(
    +    self, name, description="", **attrs,
    +):
    +    """Creates a new project circle.
    +
    +    Args:
    +        name (str): circle name
    +        description (str, optional): circle description. Defaults to "".
    +
    +    Returns:
    +        [ProjectCircle]: Project circle
    +    """
    +
    +    attrs = {
    +        "is_backlog_activated": False,
    +        "is_issues_activated": True,
    +        "is_kanban_activated": True,
    +        "is_private": False,
    +        "is_wiki_activated": True,
    +    }
    +    issues_types = ["Bug", "Question", "Enhancement"]
    +    severities = ["Wishlist", "Minor", "Normal", "Important", "Critical"]
    +    priorities = None
    +
    +    story_statuses = [
    +        "New",
    +        "to-start",
    +        "in-progress",
    +        "Blocked",
    +        "Implemented",
    +        "Verified",
    +        "Archived",
    +    ]
    +    item_statuses = ["New", "to-start", "in-progress", "Blocked", "Done"]
    +    issues_statuses = [
    +        "New",
    +        "to-start",
    +        "in-progress",
    +        "Blocked",
    +        "Implemented",
    +        "Closed",
    +        "Rejected",
    +        "Postponed",
    +        "Archived",
    +    ]
    +
    +    return ProjectCircle(
    +        self,
    +        self._create_new_circle(
    +            name,
    +            type_="project",
    +            description=description,
    +            severities=severities,
    +            issues_statuses=issues_statuses,
    +            priorities=priorities,
    +            issues_types=issues_types,
    +            user_stories_statuses=story_statuses,
    +            tasks_statuses=item_statuses,
    +            **attrs,
    +        ),
    +    )
    +
    +
    +
    +def create_new_team_circle(self, name, description='', **attrs) +
    +
    +

    Creates a new team circle. using sprints & timeline (does not use kanban)

    +

    Args

    +
    +
    name : str
    +
    circle name
    +
    description : str, optional
    +
    circle description. Defaults to "".
    +
    severities : List[str], optional
    +
    list of strings to represent severities. Defaults to None.
    +
    issues_statuses : List[str], optional
    +
    list of strings to represent issues_stauses. Defaults to None.
    +
    priorities : List[str], optional
    +
    list of strings to represent priorities. Defaults to None.
    +
    issues_types : List[str], optional
    +
    list of strings to represent issues types. Defaults to None.
    +
    user_stories_statuses : List[str], optional
    +
    list of strings to represent user stories. Defaults to None.
    +
    tasks_statuses : List[str], optional
    +
    list of strings to represent task statuses. Defaults to None.
    +
    +

    Returns

    +
    +
    [TeamCircle]
    +
    team circle
    +
    +
    + +Expand source code + +
    def create_new_team_circle(self, name, description="", **attrs):
    +    """Creates a new team circle. using sprints & timeline (does not use kanban)
    +
    +    Args:
    +        name (str): circle name
    +        description (str, optional): circle description. Defaults to "".
    +        severities (List[str], optional): list of strings to represent severities. Defaults to None.
    +        issues_statuses (List[str], optional): list of strings to represent issues_stauses. Defaults to None.
    +        priorities (List[str], optional): list of strings to represent priorities. Defaults to None.
    +        issues_types (List[str], optional): list of strings to represent issues types. Defaults to None.
    +        user_stories_statuses (List[str], optional): list of strings to represent user stories. Defaults to None.
    +        tasks_statuses (List[str], optional): list of strings to represent task statuses. Defaults to None.
    +
    +    Returns:
    +        [TeamCircle]: team circle
    +    """
    +
    +    attrs = {
    +        "is_backlog_activated": True,
    +        "is_issues_activated": True,
    +        "is_kanban_activated": False,
    +        "is_private": False,
    +        "is_wiki_activated": True,
    +    }
    +    issues_types = ["Bug", "Question", "Enhancement"]
    +    severities = ["Wishlist", "Minor", "Normal", "Important", "Critical"]
    +    priorities = None
    +
    +    story_statuses = [
    +        "New",
    +        "to-start",
    +        "in-progress",
    +        "Blocked",
    +        "Implemented",
    +        "Verified",
    +        "Archived",
    +    ]
    +    item_statuses = ["New", "to-start", "in-progress", "Blocked", "Done"]
    +    issues_statuses = [
    +        "New",
    +        "to-start",
    +        "in-progress",
    +        "Blocked",
    +        "Implemented",
    +        "Closed",
    +        "Rejected",
    +        "Postponed",
    +        "Archived",
    +    ]
    +
    +    return TeamCircle(
    +        self,
    +        self._create_new_circle(
    +            name,
    +            type_="team",
    +            description=description,
    +            severities=severities,
    +            issues_statuses=issues_statuses,
    +            priorities=priorities,
    +            issues_types=issues_types,
    +            user_stories_statuses=story_statuses,
    +            tasks_statuses=item_statuses,
    +            **attrs,
    +        ),
    +    )
    +
    +
    +
    +def credential_updated(self, value) +
    +
    +
    +
    + +Expand source code + +
    def credential_updated(self, value):
    +    self._api = None
    +
    +
    +
    +def export_as_md(self, wiki_path='/tmp/taigawiki', modified_only: bool = True, full_info=False) +
    +
    +

    export taiga instance into a wiki +showing users and circles +HINT: Using full_info will take longer time

    +

    Args

    +
    +
    wiki_src_path : str, optional
    +
    wiki path. Defaults to "/tmp/taigawiki".
    +
    modified_only : bool
    +
    write modified objects only. Defaults to True
    +
    full_info : bool
    +
    export object with full info. Defaults to False
    +
    +
    + +Expand source code + +
    def export_as_md(self, wiki_path="/tmp/taigawiki", modified_only: bool = True, full_info=False):
    +    """export taiga instance into a wiki  showing users and circles
    +    HINT: Using full_info will take longer time
    +
    +    Args:
    +        wiki_src_path (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +        modified_only (bool): write modified objects only. Defaults to True
    +        full_info (bool): export object with full info. Defaults to False
    +    """
    +    j.logger.info("Start Exporting Wiki ...")
    +    gs = []
    +    gs.append(gevent.spawn(self.export_circles_as_md, wiki_path, modified_only, full_info))
    +    gs.append(gevent.spawn(self.export_users_as_md, wiki_path, modified_only, full_info))
    +    gevent.joinall(gs)
    +
    +    template_file = j.sals.fs.join_paths(Path(__file__).parent, "template.html")
    +    index_html_path = j.sals.fs.join_paths(wiki_path, "src", "index.html")
    +    readme_md_path = j.sals.fs.join_paths(wiki_path, "src", "README.md")
    +    sidebar_md_path = j.sals.fs.join_paths(wiki_path, "src", "_sidebar.md")
    +    content = dedent(
    +        f"""
    +        # Taiga overview
    +
    +        - [circles](./circles/circles.md)
    +        - [users](./users/users.md)
    +    """
    +    )
    +    j.sals.fs.write_ascii(readme_md_path, content)
    +    j.sals.fs.write_ascii(sidebar_md_path, content)
    +    j.sals.fs.copy_file(template_file, index_html_path)
    +    j.logger.info(f"Exported at {wiki_path}")
    +
    +
    +
    +def export_as_md_periodically(self, wiki_path='/tmp/taigawiki', period: int = 300, modified_only: bool = True, full_info=False) +
    +
    +

    export taiga instance into a wiki +showing users and circles periodically +HINT: Using full_info will take longer time

    +

    Args

    +
    +
    wiki_path : str, optional
    +
    wiki path. Defaults to "/tmp/taigawiki".
    +
    period : int
    +
    Time to wait between each export in "Seconds". Defaults to 300 (5 Min).
    +
    modified_only : bool
    +
    write modified objects only.. Defaults to True.
    +
    full_info : bool
    +
    export object with full info. Defaults to False
    +
    +
    + +Expand source code + +
    def export_as_md_periodically(
    +    self, wiki_path="/tmp/taigawiki", period: int = 300, modified_only: bool = True, full_info=False
    +):
    +    """export taiga instance into a wiki  showing users and circles periodically
    +    HINT: Using full_info will take longer time
    +
    +    Args:
    +        wiki_path (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +        period (int): Time to wait between each export in "Seconds". Defaults to 300 (5 Min).
    +        modified_only (bool): write modified objects only.. Defaults to True.
    +        full_info (bool): export object with full info. Defaults to False
    +
    +    """
    +    repeater = Event()
    +    while True:
    +        j.logger.info("Start Exporting ....")
    +        self.export_as_md(wiki_path, modified_only, full_info)
    +        j.logger.info(f"Exported at {wiki_path}")
    +        repeater.wait(period)
    +
    +
    +
    +def export_as_yaml(self, export_dir='/tmp/export_dir', full_info=False) +
    +
    +

    export taiga instance [Circle, Story, Issue, Task , User] into a yaml files +HINT: Using full_info will take longer time

    +

    Args

    +
    +
    export_dir : str, optional
    +
    [description]. Defaults to "/tmp/export_dir".
    +
    full_info : bool
    +
    export object with full info. Defaults to False
    +
    +
    + +Expand source code + +
    def export_as_yaml(self, export_dir="/tmp/export_dir", full_info=False):
    +    """export taiga instance [Circle, Story, Issue, Task , User] into a yaml files
    +    HINT: Using full_info will take longer time
    +
    +    Args:
    +        export_dir (str, optional): [description]. Defaults to "/tmp/export_dir".
    +        full_info (bool): export object with full info. Defaults to False
    +    """
    +
    +    def _export_objects_to_dir(objects_dir, objects_fun, full_info):
    +        j.sals.fs.mkdirs(objects_dir)
    +        objects = objects_fun(full_info=full_info)
    +        for obj in objects:
    +            try:
    +                outpath = j.sals.fs.join_paths(objects_dir, f"{obj.id}.yaml")
    +                j.sals.fs.write_ascii(outpath, obj.as_yaml)
    +
    +            except Exception as e:
    +                import traceback
    +
    +                traceback.print_exc()
    +                j.logger.error(e)
    +                j.logger.error(f"{type(obj)}: {obj.id}")
    +
    +    projects_path = j.sals.fs.join_paths(export_dir, "projects")
    +    stories_path = j.sals.fs.join_paths(export_dir, "stories")
    +    issues_path = j.sals.fs.join_paths(export_dir, "issues")
    +    tasks_path = j.sals.fs.join_paths(export_dir, "tasks")
    +    # Milestones is not one of our model objects
    +    # milestones_path = j.sals.fs.join_paths(export_dir, "milestones")
    +    users_path = j.sals.fs.join_paths(export_dir, "users")
    +
    +    def on_err(*args, **kwargs):
    +        print("err, ", args, kwargs)
    +
    +    j.logger.info("Start Export as YAML")
    +
    +    gs = []
    +    gs.append(gevent.spawn(_export_objects_to_dir, projects_path, self.list_all_active_projects, full_info))
    +    gs.append(gevent.spawn(_export_objects_to_dir, stories_path, self.list_all_user_stories, full_info))
    +    gs.append(gevent.spawn(_export_objects_to_dir, issues_path, self.list_all_issues, full_info))
    +    # Milestones is not one of our model objects
    +    # gs.append(gevent.spawn(_export_objects_to_dir, milestones_path, self.list_all_milestones)
    +    gs.append(gevent.spawn(_export_objects_to_dir, users_path, self.list_all_users, full_info))
    +    gs.append(gevent.spawn(_export_objects_to_dir, tasks_path, self.list_all_tasks, full_info))
    +    gevent.joinall(gs)
    +
    +    j.logger.info("Finish Export as YAML")
    +
    +
    +
    +def export_circles_as_md(self, wikipath='/tmp/taigawiki', modified_only=True, full_info=False) +
    +
    +

    export circles into {wikipath}/src/circles +HINT: Using full_info will take longer time

    +

    Args

    +
    +
    wikipath : str, optional
    +
    wiki path. Defaults to "/tmp/taigawiki".
    +
    full_info : bool
    +
    export object with full info. Defaults to False
    +
    +
    + +Expand source code + +
    def export_circles_as_md(self, wikipath="/tmp/taigawiki", modified_only=True, full_info=False):
    +    """export circles into {wikipath}/src/circles
    +    HINT: Using full_info will take longer time
    +
    +    Args:
    +        wikipath (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +        full_info (bool): export object with full info. Defaults to False
    +    """
    +    path = j.sals.fs.join_paths(wikipath, "src", "circles")
    +
    +    j.sals.fs.mkdirs(path)
    +    circles = self.list_all_active_projects(full_info=full_info)
    +
    +    def write_md_for_circle(circle):
    +        circle_md = circle.as_md
    +        circle_mdpath = j.sals.fs.join_paths(path, f"{circle.clean_name}.md")
    +        if not (
    +            modified_only and j.sals.fs.exists(circle_mdpath) and j.sals.fs.read_ascii(circle_mdpath) == circle_md
    +        ):
    +            j.sals.fs.write_ascii(circle_mdpath, circle_md)
    +
    +    circles_mdpath = j.sals.fs.join_paths(path, "circles.md")
    +    circles_mdcontent = "# circles\n\n"
    +    for c in circles:
    +        circles_mdcontent += f"- [{c.name}](./{c.clean_name}.md)\n"
    +
    +    j.sals.fs.write_ascii(circles_mdpath, circles_mdcontent)
    +
    +    greenlets = [gevent.spawn(write_md_for_circle, gcircle_obj) for gcircle_obj in circles]
    +    gevent.joinall(greenlets)
    +
    +
    +
    +def export_users_as_md(self, wikipath='/tmp/taigawiki', modified_only=True, full_info=False) +
    +
    +

    export users into {wikipath}/src/users +HINT: Using full_info will take longer time

    +

    Args

    +
    +
    wikipath : str, optional
    +
    wiki path. Defaults to "/tmp/taigawiki".
    +
    modified_only : bool
    +
    export moidified objects only
    +
    full_info : bool
    +
    export object with full info. Defaults to False
    +
    +
    + +Expand source code + +
    def export_users_as_md(self, wikipath="/tmp/taigawiki", modified_only=True, full_info=False):
    +    """export users into {wikipath}/src/users
    +    HINT: Using full_info will take longer time
    +
    +    Args:
    +        wikipath (str, optional): wiki path. Defaults to "/tmp/taigawiki".
    +        modified_only (bool): export moidified objects only
    +        full_info (bool): export object with full info. Defaults to False
    +    """
    +
    +    path = j.sals.fs.join_paths(wikipath, "src", "users")
    +    j.sals.fs.mkdirs(path)
    +    users_objects = self.list_all_users(full_info=full_info)
    +
    +    users_mdpath = j.sals.fs.join_paths(path, "users.md")
    +    users_mdcontent = "# users\n\n"
    +
    +    def write_md_for_user(user):
    +        user_md = user.as_md
    +        user_mdpath = j.sals.fs.join_paths(path, f"{user.clean_name}.md")
    +        if not (modified_only and j.sals.fs.exists(user_mdpath) and j.sals.fs.read_ascii(user_mdpath) == user_md):
    +            j.sals.fs.write_ascii(user_mdpath, user_md)
    +
    +    for u in users_objects:
    +        users_mdcontent += f"- [{u.username}](./{u.clean_name}.md)\n"
    +
    +    j.sals.fs.write_ascii(users_mdpath, users_mdcontent)
    +
    +    greenlets = [gevent.spawn(write_md_for_user, guser_obj) for guser_obj in users_objects]
    +    gevent.joinall(greenlets)
    +
    +
    +
    +def get_circles_issues(self, project_id) +
    +
    +

    Get all issues in a circle/project

    +

    Args

    +
    +
    project_id : int
    +
    id of the circle/project
    +
    +

    Raises

    +
    +
    j.exceptions.NotFound
    +
    if couldn't find circle with specified id
    +
    +
    + +Expand source code + +
    def get_circles_issues(self, project_id):
    +    """Get all issues in a circle/project
    +
    +    Args:
    +        project_id (int): id of the circle/project
    +
    +    Raises:
    +        j.exceptions.NotFound: if couldn't find circle with specified id
    +    """
    +    try:
    +        circle = self.api.projects.get(project_id)
    +    except TaigaRestException:
    +        raise j.exceptions.NotFound(f"Couldn't find project with id: {project_id}")
    +
    +    circle_issues = []
    +    for issue in circle.list_issues():
    +        issue.project = self._get_project(issue.project)
    +        issue.milestone = self._get_milestone(issue.milestone)
    +        issue.priority = self._get_priority(issue.priority)
    +        issue.assignee = self._get_assignee(issue.assigned_to)
    +        issue.status = self._get_issue_status(issue.status)
    +        circle_issues.append(issue)
    +    return circle_issues
    +
    +
    +
    +def get_issue_by_id(self, issue_id) +
    +
    +

    Get issue

    +

    Args

    +
    +
    issue_id
    +
    the id of the desired issue
    +
    +

    Returns

    +
    +
    Issue object
    +
    issue
    +
    +
    + +Expand source code + +
    def get_issue_by_id(self, issue_id):
    +    """Get issue
    +    Args:
    +        issue_id: the id of the desired issue
    +
    +    Returns:
    +        Issue object: issue
    +    """
    +    return CircleIssue(self, self.api.issues.get(issue_id))
    +
    +
    +
    +def get_issue_custom_fields(self, id) +
    +
    +

    Get Issue Custom fields

    +

    Args

    +
    +
    id : int
    +
    Issue id
    +
    +

    Returns

    +
    +
    List
    +
    List of dictionaries {name: "custom field name", value: {values as dict}}
    +
    +
    + +Expand source code + +
    def get_issue_custom_fields(self, id):
    +    """Get Issue Custom fields
    +
    +    Args:
    +        id (int): Issue id
    +
    +    Returns:
    +        List: List of dictionaries {name: "custom field name", value: {values as dict}}
    +    """
    +    issue = self.api.issues.get(id)
    +    issue_attributes = issue.get_attributes()["attributes_values"]
    +    project_attributes = self._get_project(issue.project).list_issue_attributes()
    +    custom_fields = []
    +    for p_attr in project_attributes:
    +        for k, value in issue_attributes.items():
    +            if p_attr.id == int(k):
    +                try:
    +                    custom_fields.append({"name": p_attr.name, "value": yaml.full_load(value)})
    +                except:
    +                    custom_fields.append({"name": p_attr.name, "value": value})
    +                break
    +
    +    return custom_fields
    +
    +
    +
    +def get_story_custom_fields(self, id) +
    +
    +

    Get User_Story Custom fields

    +

    Args

    +
    +
    id : int
    +
    User_Story id
    +
    +

    Returns

    +
    +
    List
    +
    List of dictionaries {name: "custom field name", value: {values as dict}}
    +
    +
    + +Expand source code + +
    def get_story_custom_fields(self, id):
    +    """Get User_Story Custom fields
    +
    +    Args:
    +        id (int): User_Story id
    +
    +    Returns:
    +        List: List of dictionaries {name: "custom field name", value: {values as dict}}
    +    """
    +    user_story = self.api.user_stories.get(id)
    +    user_story_attributes = user_story.get_attributes()["attributes_values"]
    +    project_attributes = self._get_project(user_story.project).list_user_story_attributes()
    +    custom_fields = []
    +    for p_attr in project_attributes:
    +        for k, value in user_story_attributes.items():
    +            if p_attr.id == int(k):
    +                try:
    +                    custom_fields.append({"name": p_attr.name, "value": yaml.full_load(value)})
    +                except:
    +                    custom_fields.append({"name": p_attr.name, "value": value})
    +                break
    +
    +    return custom_fields
    +
    +
    +
    +def get_user_circles(self, username) +
    +
    +

    Get circles owned by user

    +

    Args

    +
    +
    username : str
    +
    Name of the user
    +
    +
    + +Expand source code + +
    def get_user_circles(self, username):
    +    """Get circles owned by user
    +
    +    Args:
    +        username (str): Name of the user
    +    """
    +    user_id = self._get_user_id(username)
    +    circles = self.api.projects.list(member=user_id)
    +    user_circles = []
    +    for circle in circles:
    +        if circle.owner["id"] == user_id:
    +            user_circles.append(self._resolve_object(circle))
    +    return user_circles
    +
    +
    +
    +def get_user_stories(self, username) +
    +
    +

    Get all stories of a user

    +

    Args

    +
    +
    username : str
    +
    Name of the user
    +
    +
    + +Expand source code + +
    def get_user_stories(self, username):
    +    """Get all stories of a user
    +
    +    Args:
    +        username (str): Name of the user
    +    """
    +    user_id = self._get_user_id(username)
    +    user_stories = self.api.user_stories.list(assigned_to=user_id)
    +    user_stories = []
    +    for user_story in user_stories:
    +        # user_story.project = self._get_project(user_story.project)
    +        # user_story.milestone = self._get_milestone(user_story.milestone)
    +        user_story.status = self._get_user_stories_status(user_story.status)
    +        user_stories.append(user_story)
    +    return user_stories
    +
    +
    +
    +def get_user_tasks(self, username) +
    +
    +

    Get all tasks of a user

    +

    Args

    +
    +
    username : str
    +
    Name of the user
    +
    +
    + +Expand source code + +
    def get_user_tasks(self, username):
    +    """Get all tasks of a user
    +
    +    Args:
    +        username (str): Name of the user
    +    """
    +    user_id = self._get_user_id(username)
    +    user_tasks = self.api.tasks.list(assigned_to=user_id)
    +    user_tasks = []
    +    for user_task in user_tasks:
    +        # user_task.project = self._get_project(user_task.project)
    +        # user_task.milestone = self._get_milestone(user_task.milestone)
    +        user_task.status = self._get_task_status(user_task.status)
    +        user_tasks.append(self._resolve_object(user_task))
    +
    +    return user_tasks
    +
    +
    +
    +def import_from_yaml(self, import_dir='/tmp/export_dir') +
    +
    +

    Import Circle with all stories, issues and tasks from yaml files

    +

    Args

    +
    +
    import_dir : str
    +
    import directory path. Defaults to "/tmp/export_dir".
    +
    +
    + +Expand source code + +
    def import_from_yaml(self, import_dir="/tmp/export_dir"):
    +    """Import Circle with all stories, issues and tasks from yaml files
    +
    +    Args:
    +        import_dir (str): import directory path. Defaults to "/tmp/export_dir".
    +    """
    +    # Helper Functions
    +    def check_by_name(list_obj, name_to_find):
    +        for obj in list_obj:
    +            if obj.name == name_to_find:
    +                return obj.id
    +
    +        return list_obj[0].id
    +
    +    def import_circle(self, yaml_obj):
    +        circle = None
    +        # Funnel Circle
    +        if yaml_obj["basic_info"]["name"].lower() == "funnel":
    +            circle = self.create_new_funnel_circle(
    +                yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +            )
    +        # Team Circle
    +        elif yaml_obj["basic_info"]["name"].lower() == "team":
    +            circle = self.create_new_team_circle(
    +                yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +            )
    +        # Project Circle
    +        elif yaml_obj["basic_info"]["name"].lower() == "project":
    +            circle = self.create_new_project_circle(
    +                yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],
    +            )
    +        # Any Other Circle
    +        else:
    +            circle = self._create_new_circle(yaml_obj["basic_info"]["name"], yaml_obj["basic_info"]["description"],)
    +            circle.is_backlog_activated = yaml_obj["modules"]["is_backlog_activated"]
    +            circle.is_issues_activated = yaml_obj["modules"]["is_issues_activated"]
    +            circle.is_kanban_activated = yaml_obj["modules"]["is_kanban_activated"]
    +            circle.is_wiki_activated = yaml_obj["modules"]["is_wiki_activated"]
    +
    +        circle.is_private = yaml_obj["basic_info"]["is_private"]
    +        circle.videoconferences = yaml_obj["modules"]["videoconferences"]
    +        for issue_attr in yaml_obj["issues_attributes"]:
    +            circle.add_issue_attribute(issue_attr)
    +        for us_attr in yaml_obj["stories_attributes"]:
    +            circle.add_user_story_attribute(us_attr)
    +        return circle
    +
    +    def import_story(circle_object, yaml_obj):
    +        status_id = check_by_name(circle_object.us_statuses, yaml_obj["status"]["name"])
    +
    +        story = circle_object.add_user_story(
    +            yaml_obj["basic_info"].get("subject"),
    +            tags=yaml_obj["basic_info"].get("tags"),
    +            description=yaml_obj["basic_info"].get("description", ""),
    +            client_requirement=yaml_obj["requirements"].get("client_requirement"),
    +            team_requirement=yaml_obj["requirements"].get("team_requirement"),
    +            is_blocked=yaml_obj["additional_info"].get("is_blocked"),
    +            due_date=yaml_obj["date"].get("due_date"),
    +            status=status_id,
    +        )
    +
    +        for field in yaml_obj.get("custom_fields", []):
    +            for attr in circle_object.list_user_story_attributes():
    +                if attr.name == field["name"]:
    +                    story.set_attribute(attr.id, field["value"])
    +                    break
    +        return story
    +
    +    def import_issue(circle_object, yaml_obj):
    +        priority_id = check_by_name(circle_object.priorities, yaml_obj["priority"]["name"])
    +        status_id = check_by_name(circle_object.issue_statuses, yaml_obj["status"]["name"])
    +        type_id = check_by_name(circle_object.issue_types, yaml_obj["type"]["name"])
    +        severity_id = check_by_name(circle_object.severities, yaml_obj["severity"]["name"])
    +        issue = circle_object.add_issue(
    +            yaml_obj["basic_info"]["subject"],
    +            priority_id,
    +            status_id,
    +            type_id,
    +            severity_id,
    +            description=yaml_obj["basic_info"].get("description"),
    +        )
    +
    +        for field in yaml_obj["custom_fields"]:
    +            for attr in circle_object.list_issue_attributes():
    +                if attr.name == field["name"]:
    +                    issue.set_attribute(attr.id, field["value"])
    +                    break
    +        return issue
    +
    +    def import_tasks(circle_object, story_object, yaml_obj):
    +        status_id = check_by_name(circle_object.task_statuses, yaml_obj["status"]["name"])
    +        task = story_object.add_task(
    +            yaml_obj["basic_info"].get("subject"),
    +            status_id,
    +            description=yaml_obj["basic_info"].get("description", ""),
    +            tags=yaml_obj["basic_info"].get("tags"),
    +        )
    +        return task
    +
    +    # Folders Path
    +    projects_path = j.sals.fs.join_paths(import_dir, "projects")
    +    stories_path = j.sals.fs.join_paths(import_dir, "stories")
    +    issues_path = j.sals.fs.join_paths(import_dir, "issues")
    +    tasks_path = j.sals.fs.join_paths(import_dir, "tasks")
    +
    +    # List of Files inside project Folder
    +    projects = j.sals.fs.os.listdir(projects_path)
    +
    +    for project_file in projects:
    +        if project_file.endswith(".yaml") or project_file.endswith(".yml"):
    +            with open(j.sals.fs.join_paths(projects_path, project_file)) as pf:
    +                circle_yaml = yaml.full_load(pf)
    +                circle_obj = import_circle(self, circle_yaml)
    +                j.logger.info(f"<Circle {circle_obj.id} Created>")
    +
    +                for story in circle_yaml["stories"]:
    +                    with open(j.sals.fs.join_paths(stories_path, f"{story}.yaml")) as sf:
    +                        story_yaml = yaml.full_load(sf)
    +                        story_obj = import_story(circle_obj, story_yaml)
    +                        j.logger.info(f"<Story {story_obj.id} Created in Circle {circle_obj.id}>")
    +
    +                        for task in story_yaml["tasks"]:
    +                            with open(j.sals.fs.join_paths(tasks_path, f"{task}.yaml")) as tf:
    +                                task_yaml = yaml.full_load(tf)
    +                                task_obj = import_tasks(circle_obj, story_obj, task_yaml)
    +                                j.logger.info(f"<Task {task_obj.id} Created in Story {story_obj.id}>")
    +                                task_obj.update()
    +                        story_obj.update()
    +
    +                for issue in circle_yaml["issues"]:
    +                    with open(j.sals.fs.join_paths(issues_path, f"{issue}.yaml")) as isf:
    +                        issue_yaml = yaml.full_load(isf)
    +                        issue_obj = import_issue(circle_obj, issue_yaml)
    +                        j.logger.info(f"<Issue {issue_obj.id} Created in Circle {circle_obj.id}>")
    +                        issue_obj.update()
    +
    +                circle_obj.update()
    +                j.logger.info(f"<Circle {circle_obj.id} Imported with All Stories and Issues")
    +
    +
    +
    +def list_all_active_projects(self, full_info=False) +
    +
    +

    List all projects not starting with "ARHCIVE" +HINT: Using full_info will take a longer time

    +

    Args

    +
    +
    full_info : bool
    +
    [description]. Defaults to False.
    +
    +

    Returns

    +
    +
    [type]
    +
    [description]
    +
    +
    + +Expand source code + +
    def list_all_active_projects(self, full_info=False):
    +    """
    +    List all projects not starting with "ARHCIVE"
    +    HINT: Using full_info will take a longer time
    +
    +    Args:
    +        full_info (bool): [description]. Defaults to False.
    +
    +    Returns:
    +        [type]: [description]
    +    """
    +    return [
    +        Circle(self, p)
    +        for p in self.list_projects_by(lambda x: not x.name.startswith("ARCHIVE_"), full_info=full_info)
    +    ]
    +
    +
    +
    +def list_all_issues(self, username='', full_info=False) +
    +
    +

    List all issues for specific user if you didn't pass user_id will list all the issues +HINT: Using full_info will take a longer time

    +

    Args

    +
    +
    username : str
    +
    username.
    +
    full_info : bool
    +
    flag used to get object with full info. Defaults to False.
    +
    +

    Returns

    +
    +
    List
    +
    List of taiga.models.models.Issue.
    +
    +
    + +Expand source code + +
    def list_all_issues(self, username="", full_info=False):
    +    """
    +    List all issues for specific user if you didn't pass user_id will list all the issues
    +    HINT: Using full_info will take a longer time
    +
    +    Args:
    +        username (str): username.
    +        full_info (bool): flag used to get object with full info. Defaults to False.
    +
    +    Returns:
    +        List: List of taiga.models.models.Issue.
    +    """
    +    if username:
    +        user_id = self._get_user_id(username)
    +        if not full_info:
    +            return [CircleIssue(self, self._resolve_object(x)) for x in self.api.issues.list(assigned_to=user_id)]
    +        else:
    +            return [CircleIssue(self, self.api.issues.get(x.id)) for x in self.api.issues.list(assigned_to=user_id)]
    +    else:
    +        if not full_info:
    +            return [CircleIssue(self, self._resolve_object(x)) for x in self.api.issues.list()]
    +        else:
    +            return [CircleIssue(self, self.api.issues.get(x.id)) for x in self.api.issues.list()]
    +
    +
    +
    +def list_all_milestones(self) +
    +
    +

    List all milestones

    +

    Returns

    +
    +
    List
    +
    List of taiga.models.models.Milestone.
    +
    +
    + +Expand source code + +
    def list_all_milestones(self):
    +    """
    +    List all milestones
    +
    +    Returns:
    +        List: List of taiga.models.models.Milestone.
    +    """
    +    return [self._resolve_object(x) for x in self.api.milestones.list()]
    +
    +
    +
    +def list_all_projects(self, full_info=False) +
    +
    +

    List all projects +HINT: Using full_info will take a longer time

    +

    Args

    +

    full_info(bool): flag used to get object with full info. Defaults to False.

    +

    Returns

    +
    +
    List
    +
    List of taiga.models.models.Project.
    +
    +
    + +Expand source code + +
    def list_all_projects(self, full_info=False):
    +    """
    +    List all projects
    +    HINT: Using full_info will take a longer time
    +
    +    Args:
    +        full_info(bool): flag used to get object with full info. Defaults to False.
    +
    +    Returns:
    +        List: List of taiga.models.models.Project.
    +    """
    +    if not full_info:
    +        return [Circle(self, self._resolve_object(x)) for x in self.api.projects.list()]
    +    else:
    +        return [Circle(self, self.api.projects.get(x.id)) for x in self.api.projects.list()]
    +
    +
    +
    +def list_all_tasks(self, username='', full_info=False) +
    +
    +

    List all tasks for specific user if you didn't pass user_id will list all the tasks +HINT: Using full_info will take a longer time

    +

    Args

    +
    +
    username : str
    +
    username.
    +
    full_info : bool
    +
    flag used to get object with full info. Defaults to False.
    +
    +

    Returns

    +
    +
    List
    +
    List of taiga.models.models.Task.
    +
    +
    + +Expand source code + +
    def list_all_tasks(self, username="", full_info=False):
    +    """
    +    List all tasks for specific user if you didn't pass user_id will list all the tasks
    +    HINT: Using full_info will take a longer time
    +
    +    Args:
    +        username (str): username.
    +        full_info (bool): flag used to get object with full info. Defaults to False.
    +
    +    Returns:
    +        List: List of taiga.models.models.Task.
    +    """
    +    if username:
    +        user_id = self._get_user_id(username)
    +        if not full_info:
    +            return [CircleTask(self, self._resolve_object(x)) for x in self.api.tasks.list(assigned_to=user_id)]
    +        else:
    +            return [CircleTask(self, self.api.tasks.get(x.id)) for x in self.api.tasks.list(assigned_to=user_id)]
    +    else:
    +        if not full_info:
    +            return [CircleTask(self, self._resolve_object(x)) for x in self.api.tasks.list()]
    +        else:
    +            return [CircleTask(self, self.api.tasks.get(x.id)) for x in self.api.tasks.list()]
    +
    +
    +
    +def list_all_user_stories(self, username='', full_info=False) +
    +
    +

    List all user stories for specific user if you didn't pass user_id will list all the available user stories +HINT: Using full_info will take a longer time

    +

    Args

    +
    +
    username : str
    +
    username.
    +
    +

    full_info(bool): flag used to get object with full info. Defaults to False

    +

    Returns

    +
    +
    List
    +
    List of CircleStory.
    +
    +
    + +Expand source code + +
    def list_all_user_stories(self, username="", full_info=False):
    +    """
    +    List all user stories for specific user if you didn't pass user_id will list all the available user stories
    +    HINT: Using full_info will take a longer time
    +
    +    Args:
    +        username (str): username.
    +        full_info(bool): flag used to get object with full info. Defaults to False
    +
    +    Returns:
    +        List: List of CircleStory.
    +    """
    +    if username:
    +        user_id = self._get_user_id(username)
    +        if not full_info:
    +            return [
    +                CircleStory(self, self._resolve_object(x)) for x in self.api.user_stories.list(assigned_to=user_id)
    +            ]
    +        else:
    +            return [
    +                CircleStory(self, self.api.user_stories.get(x.id))
    +                for x in self.api.user_stories.list(assigned_to=user_id)
    +            ]
    +    else:
    +        if not full_info:
    +            return [CircleStory(self, self._resolve_object(x)) for x in self.api.user_stories.list()]
    +        else:
    +            return [CircleStory(self, self.api.user_stories.get(x.id)) for x in self.api.user_stories.list()]
    +
    +
    +
    +def list_all_users(self, full_info=False) +
    +
    +

    List all user stories for specific user if you didn't pass user_id will list all the available user stories +HINT: Using full_info will take a longer time

    +

    Args

    +
    +
    username : str
    +
    username.
    +
    +

    full_info(bool): flag used to get object with full info. Defaults to False

    +

    Returns

    +
    +
    List
    +
    List of CircleUser.
    +
    +
    + +Expand source code + +
    def list_all_users(self, full_info=False):
    +    """
    +    List all user stories for specific user if you didn't pass user_id will list all the available user stories
    +    HINT: Using full_info will take a longer time
    +    Args:
    +        username (str): username.
    +        full_info(bool): flag used to get object with full info. Defaults to False
    +
    +    Returns:
    +        List: List of CircleUser.
    +    """
    +    circles = self.list_all_projects()
    +    users = set()
    +    for c in circles:
    +        for m in c.members:
    +            users.add(m)
    +
    +    return [CircleUser(self, self._get_user_by_id(uid)) for uid in users]
    +
    +
    +
    +def list_funnel_circles(self) +
    +
    +
    +
    + +Expand source code + +
    def list_funnel_circles(self):
    +    return [FunnelCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("FUNNEL_"))]
    +
    +
    +
    +def list_project_circles(self) +
    +
    +
    +
    + +Expand source code + +
    def list_project_circles(self):
    +    return [ProjectCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("PROJECT_"))]
    +
    +
    +
    +def list_projects_by(self, fn=<function TaigaClient.<lambda>>, full_info=False) +
    +
    +
    +
    + +Expand source code + +
    def list_projects_by(self, fn=lambda x: True, full_info=False):
    +    return [p for p in self.list_all_projects(full_info=full_info) if fn(p)]
    +
    +
    +
    +def list_team_circles(self) +
    +
    +
    +
    + +Expand source code + +
    def list_team_circles(self):
    +    return [TeamCircle(self, p) for p in self.list_projects_by(lambda x: x.name.startswith("TEAM_"))]
    +
    +
    +
    +def move_story_to_circle(self, story_id, project_id) +
    +
    +

    Moves a story to another circle/project

    +

    Args

    +
    +
    story_id : int
    +
    User story id
    +
    project_id : int
    +
    circle/project id
    +
    +

    Raises

    +
    +
    j.exceptions.NotFound
    +
    No user story with specified id found
    +
    j.exceptions.NotFound
    +
    No project with specified id found
    +
    j.exceptions.Runtime
    +
    [description]
    +
    +

    Returns

    +
    +
    int
    +
    New id of the migrated user story
    +
    +
    + +Expand source code + +
    def move_story_to_circle(self, story_id, project_id):
    +    """Moves a story to another circle/project
    +
    +    Args:
    +        story_id (int): User story id
    +        project_id (int): circle/project id
    +
    +    Raises:
    +        j.exceptions.NotFound: No user story with specified id found
    +        j.exceptions.NotFound: No project with specified id found
    +        j.exceptions.Runtime: [description]
    +
    +    Returns:
    +        int: New id of the migrated user story
    +    """
    +
    +    def _get_project_status(project_statuses, status):
    +        for project_status in project_statuses:
    +            if project_status.name == status:
    +                return project_status.id
    +
    +    try:
    +        user_story = self.api.user_stories.get(story_id)
    +    except TaigaRestException:
    +        raise j.exceptions.NotFound(f"Couldn't find user story with id: {story_id}")
    +
    +    project_stories_statuses = self.api.user_story_statuses.list(project=project_id)
    +    status = self._get_user_stories_status(user_story.status)
    +    story_status_id = _get_project_status(project_stories_statuses, status)
    +
    +    try:
    +        migrate_story = self.api.user_stories.create(
    +            project=project_id,
    +            subject=user_story.subject,
    +            assigned_to=user_story.assigned_to,
    +            milestone=user_story.milestone,
    +            status=story_status_id,
    +            tags=user_story.tags,
    +        )
    +    except TaigaRestException:
    +        raise j.exceptions.NotFound(f"No project with id: {project_id} found")
    +    try:
    +        comments = self.api.history.user_story.get(story_id)
    +        comments = sorted(comments, key=lambda c: dateutil.parser.isoparse(c["created_at"]))
    +
    +        for comment in comments:
    +            migrate_story.add_comment(comment["comment_html"])
    +
    +        project_tasks_statuses = self.api.task_statuses.list(project=project_id)
    +        for task in user_story.list_tasks():
    +            status = self._get_task_status(task.status)
    +            task_status_id = _get_project_status(project_tasks_statuses, status)
    +            migrate_task = migrate_story.add_task(
    +                subject=task.subject,
    +                status=task_status_id,
    +                due_date=task.due_date,
    +                milestone=task.milestone,
    +                assigned_to=task.assigned_to,
    +                tags=task.tags,
    +                project=migrate_story.project,
    +                user_story=migrate_story.id,
    +            )
    +            comments = self.api.history.task.get(migrate_task.id)
    +            comments = sorted(comments, key=lambda c: dateutil.parser.isoparse(c["created_at"]))
    +
    +            for comment in comments:
    +                migrate_task.add_comment(comment["comment_html"])
    +
    +    except Exception as e:
    +        self.api.user_stories.delete(migrate_story.id)
    +        raise j.exceptions.Runtime(f"Failed to migrate story error was: {str(e)}")
    +
    +    self.api.user_stories.delete(story_id)
    +    return migrate_story.id
    +
    +
    +
    +def validate_custom_fields(self, attributes) +
    +
    +

    Validate custom fields values to match our requirements

    +

    Args

    +
    +
    attributes : List
    +
    Output from get_issue/story_custom_fields functions
    +
    +

    Raises

    +
    +
    j.exceptions.Validation
    +
    Raise validation exception if any input not valid
    +
    +

    Returns

    +
    +
    bool
    +
    Return True if no exception raised and print logs
    +
    +
    + +Expand source code + +
    def validate_custom_fields(self, attributes):
    +    """Validate custom fields values to match our requirements
    +
    +    Args:
    +        attributes (List): Output from get_issue/story_custom_fields functions
    +
    +    Raises:
    +        j.exceptions.Validation: Raise validation exception if any input not valid
    +
    +    Returns:
    +        bool: Return True if no exception raised and print logs
    +    """
    +
    +    for attr in attributes:
    +        name = attr.get("name")
    +        value = attr.get("value")
    +
    +        period = value.get("period", "onetime")
    +        duration = value.get("duration", 1)
    +        amount = value.get("amount", 0)
    +        currency = value.get("currency", "eur")
    +        start_date = value.get("start_date", f"{dateutil.utils.today().month}:{dateutil.utils.today().year}",)
    +        confidence = value.get("confidence", 100)
    +        user = value.get("user")
    +        part = value.get("part", "0%")
    +        type = value.get("type", "revenue")
    +
    +        if name not in ["bookings", "commission"]:
    +            raise j.exceptions.Validation(
    +                f'Name: ({name}) is unknown custom field, please select one of the following ["bookings", "commission"]'
    +            )
    +
    +        if period not in ["onetime", "month", "year"]:
    +            raise j.exceptions.Validation(
    +                f'Period: ({period}) not found, please select one of following ["onetime", "month", "year"]'
    +            )
    +
    +        if duration < 1 or duration > 120:
    +            raise j.exceptions.Validation(f"Duration: ({duration}) is not in range, please select it from 1 to 120")
    +
    +        if not isinstance(amount, int):
    +            raise j.exceptions.Validation(f"Amount: ({amount}) is not integer, please add int value")
    +
    +        if currency.replace(" ", "").lower() not in [
    +            "usd",
    +            "chf",
    +            "eur",
    +            "gbp",
    +            "egp",
    +        ]:
    +            raise j.exceptions.Validation(
    +                f'Currency: ({currency}) is not supported, please use one of the following currencies ["usd", "chf", "eur", "gbp", "egp"]'
    +            )
    +        try:
    +            date = start_date.split(":")
    +            month = int(date[0])
    +            year = int(date[1]) if len(date) > 1 else dateutil.utils.today().year
    +            if month < 1 or month > 12:
    +                raise j.exceptions.Validation(
    +                    "Please use values from 1 to 12 in Month field, follow format like MONTH:YEAR as 11:2020 or MONTH as 11"
    +                )
    +        except ValueError as e:
    +            raise j.exceptions.Validation(
    +                "Please use numeric date with the following format MONTH:YEAR as 11:2020 or MONTH as 11"
    +            )
    +        except AttributeError as e:
    +            pass  # Will check what happen if start_date not provide
    +
    +        if confidence % 10 != 0:
    +            j.exceptions.Validation(f"Confidence: ({confidence}) not multiple of 10, it must be multiple of 10")
    +
    +        part_tmp = part.replace("%", "")
    +        if user != None and user not in self.list_all_users():
    +            raise j.exceptions.Validation(f"User: ({user}) is not found")
    +
    +        if int(part_tmp) < 0 or int(part_tmp) > 100:
    +            j.exceptions.Validation(f"Part: ({part}) is a not a valid percentage, it must be from 0% to 100%")
    +
    +        if type not in ["revenue", "booking"]:
    +            raise j.exceptions.Validation(
    +                f'Type: ({type}) is not supported type, please choose one of the following ["revenue" , "booking"]'
    +            )
    +
    +        j.logger.info(f"Attribute: {name} passed")
    +
    +    return True
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/zdb/client.html b/docs/api/jumpscale/clients/zdb/client.html index 9b5608256..123460c1f 100644 --- a/docs/api/jumpscale/clients/zdb/client.html +++ b/docs/api/jumpscale/clients/zdb/client.html @@ -1613,4 +1613,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/zdb/index.html b/docs/api/jumpscale/clients/zdb/index.html index 9d5157acf..870e1d714 100644 --- a/docs/api/jumpscale/clients/zdb/index.html +++ b/docs/api/jumpscale/clients/zdb/index.html @@ -168,4 +168,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/zerotier/index.html b/docs/api/jumpscale/clients/zerotier/index.html new file mode 100644 index 000000000..531d39e09 --- /dev/null +++ b/docs/api/jumpscale/clients/zerotier/index.html @@ -0,0 +1,101 @@ + + + + + + +jumpscale.clients.zerotier API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.zerotier

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .zerotier import ZerotierClient
    +
    +    return StoredFactory(ZerotierClient)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.clients.zerotier.zerotier
    +
    +

    A client that gives an interface to the zerotier api …

    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +
    +    from .zerotier import ZerotierClient
    +
    +    return StoredFactory(ZerotierClient)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/clients/zerotier/zerotier.html b/docs/api/jumpscale/clients/zerotier/zerotier.html new file mode 100644 index 000000000..25f1ba5b0 --- /dev/null +++ b/docs/api/jumpscale/clients/zerotier/zerotier.html @@ -0,0 +1,1095 @@ + + + + + + +jumpscale.clients.zerotier.zerotier API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.clients.zerotier.zerotier

    +
    +
    +

    A client that gives an interface to the zerotier api

    +
    zcl = j.clients.zerotier.get("instance")
    +
    +network = zcl.get_network("network_id")
    +
    +#  list routes
    +network.routes
    +
    +# add members
    +
    +member = network.add_member(address, name="guest")
    +
    +# Authorize member to this network
    +member.authorize()
    +
    +
    +
    + +Expand source code + +
    """
    +A client that gives an interface to the zerotier api
    +
    +```python
    +zcl = j.clients.zerotier.get("instance")
    +
    +network = zcl.get_network("network_id")
    +
    +#  list routes
    +network.routes
    +
    +# add members
    +
    +member = network.add_member(address, name="guest")
    +
    +# Authorize member to this network
    +member.authorize()
    +
    +```
    +
    +"""
    +
    +
    +import requests
    +import ipaddress
    +from jumpscale.loader import j
    +from jumpscale.clients.base import Client
    +from jumpscale.core.base import fields
    +
    +
    +class Member:
    +    def __init__(self, member_data, network, address=None):
    +        self.raw_data = member_data
    +        self._address = address
    +        self._network = network
    +        self.id = member_data["id"]
    +
    +    @property
    +    def address(self):
    +        if not self._address:
    +            self._address = self.identity.split(":")[0]
    +        return self._address
    +
    +    @property
    +    def identity(self):
    +        return self.raw_data["config"]["identity"]
    +
    +    @property
    +    def private_ip(self):
    +        return self.raw_data["config"]["ipAssignments"]
    +
    +    def _update_authorization(self, authorize):
    +        if self.raw_data["config"]["authorized"] == authorize:
    +            return
    +        self._network.update_member(self.address, authorized=authorize)
    +        self.raw_data["config"]["authorized"] = authorize
    +
    +    def authorize(self):
    +        """Authorize member to the zerotier network
    +        """
    +        self._update_authorization(True)
    +
    +    def unauthorize(self):
    +        """Unauthorize member to the zerotier network
    +        """
    +        self._update_authorization(False)
    +
    +    def __repr__(self):
    +        return f"Member({self.id})"
    +
    +
    +class Network:
    +    def __init__(self, network_data, client):
    +        self.id = network_data["id"]
    +        self._client = client
    +        self.raw_data = network_data
    +
    +    @property
    +    def name(self):
    +        return self.raw_data["config"]["name"]
    +
    +    @property
    +    def routes(self):
    +        return self.raw_data["config"]["routes"]
    +
    +    def list_members(self):
    +        """List all members of  that network
    +
    +        Returns:
    +            list: List of all members
    +        """
    +        return [Member(member, self) for member in self._client._send_request(f"network/{self.id}/member")]
    +
    +    def add_member(self, address, name=None, private_ip=None, authorized=True):
    +        """Add a member to the network
    +
    +        Args:
    +            address (str): Address of the member
    +            name (str, optional): Name of the member. Defaults to None.
    +            private_ip (str, optional): Private IP to assign. Defaults to None.
    +            authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    +
    +        Returns:
    +            Member: Added member
    +        """
    +        data = {"config": {"authorized": authorized}, "name": name}
    +        if private_ip:
    +            data["config"]["ipAssignments"] = [private_ip]
    +        member_data = self._client._send_request(f"network/{self.id}/member/{address}", method="post", data=data)
    +        return Member(member_data, self, address)
    +
    +    def update_member(self, address, name=None, private_ip=None, authorized=None):
    +        """Update a member in the network
    +
    +        Args:
    +            address (str): Address of the member
    +            name (str, optional): Name of the member. Defaults to None.
    +            private_ip (str, optional): Private IP to assign. Defaults to None.
    +            authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    +
    +        Returns:
    +            Member: Added member
    +        """
    +        return self.add_member(address, name, private_ip, authorized)
    +
    +    def get_member(self, address):
    +        """Returns member by address
    +
    +        Args:
    +            address (str): Member address
    +
    +        Returns:
    +            Member: Found member
    +        """
    +        return Member(self._client._send_request(f"network/{self.id}/member/{address}"), self, address)
    +
    +    def delete_member(self, address):
    +        """Deletes a member
    +
    +        Args:
    +            address (str): Member address
    +        """
    +        self._client._send_request(f"network/{self.id}/member/{address}", method="delete", return_json=False)
    +
    +    def __repr__(self):
    +        return f"Network({self.id})"
    +
    +
    +class ZerotierClient(Client):
    +    base_url = fields.String(default="https://my.zerotier.com/api")
    +    token = fields.String()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._session = None
    +
    +    def _send_request(self, path, method="get", data=None, return_json=True):
    +        url = f"{self.base_url}/{path}"
    +        func_method = getattr(self.session, method)
    +        res = func_method(url, json=data)
    +        res.raise_for_status()
    +        if return_json:
    +            return res.json()
    +        else:
    +            return res.content
    +
    +    @property
    +    def session(self):
    +        if not self._session:
    +            if not self.token:
    +                raise j.exceptions.Value("Please set token to use the client")
    +            self._session = requests.Session()
    +            self._session.headers["Authorization"] = f"Bearer {self.token}"
    +        return self._session
    +
    +    def list_networks(self):
    +        """List networks available to user
    +
    +        Returns:
    +            list: All available networks
    +        """
    +        return [Network(network, self) for network in self._send_request("network")]
    +
    +    def get_network(self, network_id):
    +        """Get network by id
    +
    +        Args:
    +            network_id (str): Network id
    +
    +        Returns:
    +            Network: Found network
    +        """
    +        return Network(self._send_request(f"network/{network_id}"), self)
    +
    +    def create_network(self, public, target_subnet=None, name=None, auto_assign=True):
    +        """Create a new network
    +
    +        Args:
    +            public (bool): Specify if network is public or not
    +            target_subnet (str, optional): Target network to be pick assignment ips from. Defaults to None.
    +            name (str, optional): Name of the network. Defaults to None.
    +            auto_assign (bool, optional): If true auto assign addresses. Defaults to True.
    +
    +        Returns:
    +            Network: Created network
    +        """
    +        routes = None
    +        config = {"private": not public, "noAutoAssignIps": not auto_assign, "routes": routes}
    +        if target_subnet:
    +            routes.append({"target": target_subnet, "via": None})
    +            network = ipaddress.IPv4Network(target_subnet)
    +            config["ipAssignmentPools"] = [{"ipRangeStart": network[0], "ipRangeEnd": network[-1]}]
    +
    +        if name:
    +            config.update({"name": name})
    +
    +        data = {"config": config}
    +        return Network(self._send_request(f"network", data=data, method="post"), self)
    +
    +    def delete_network(self, network_id):
    +        """Deletes network
    +
    +        Args:
    +            network_id (str): Network id
    +        """
    +        self._send_request(f"network/{network_id}", method="delete", return_json=False)
    +
    +    def get_status(self):
    +        return self._send_request("status")
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Member +(member_data, network, address=None) +
    +
    +
    +
    + +Expand source code + +
    class Member:
    +    def __init__(self, member_data, network, address=None):
    +        self.raw_data = member_data
    +        self._address = address
    +        self._network = network
    +        self.id = member_data["id"]
    +
    +    @property
    +    def address(self):
    +        if not self._address:
    +            self._address = self.identity.split(":")[0]
    +        return self._address
    +
    +    @property
    +    def identity(self):
    +        return self.raw_data["config"]["identity"]
    +
    +    @property
    +    def private_ip(self):
    +        return self.raw_data["config"]["ipAssignments"]
    +
    +    def _update_authorization(self, authorize):
    +        if self.raw_data["config"]["authorized"] == authorize:
    +            return
    +        self._network.update_member(self.address, authorized=authorize)
    +        self.raw_data["config"]["authorized"] = authorize
    +
    +    def authorize(self):
    +        """Authorize member to the zerotier network
    +        """
    +        self._update_authorization(True)
    +
    +    def unauthorize(self):
    +        """Unauthorize member to the zerotier network
    +        """
    +        self._update_authorization(False)
    +
    +    def __repr__(self):
    +        return f"Member({self.id})"
    +
    +

    Instance variables

    +
    +
    var address
    +
    +
    +
    + +Expand source code + +
    @property
    +def address(self):
    +    if not self._address:
    +        self._address = self.identity.split(":")[0]
    +    return self._address
    +
    +
    +
    var identity
    +
    +
    +
    + +Expand source code + +
    @property
    +def identity(self):
    +    return self.raw_data["config"]["identity"]
    +
    +
    +
    var private_ip
    +
    +
    +
    + +Expand source code + +
    @property
    +def private_ip(self):
    +    return self.raw_data["config"]["ipAssignments"]
    +
    +
    +
    +

    Methods

    +
    +
    +def authorize(self) +
    +
    +

    Authorize member to the zerotier network

    +
    + +Expand source code + +
    def authorize(self):
    +    """Authorize member to the zerotier network
    +    """
    +    self._update_authorization(True)
    +
    +
    +
    +def unauthorize(self) +
    +
    +

    Unauthorize member to the zerotier network

    +
    + +Expand source code + +
    def unauthorize(self):
    +    """Unauthorize member to the zerotier network
    +    """
    +    self._update_authorization(False)
    +
    +
    +
    +
    +
    +class Network +(network_data, client) +
    +
    +
    +
    + +Expand source code + +
    class Network:
    +    def __init__(self, network_data, client):
    +        self.id = network_data["id"]
    +        self._client = client
    +        self.raw_data = network_data
    +
    +    @property
    +    def name(self):
    +        return self.raw_data["config"]["name"]
    +
    +    @property
    +    def routes(self):
    +        return self.raw_data["config"]["routes"]
    +
    +    def list_members(self):
    +        """List all members of  that network
    +
    +        Returns:
    +            list: List of all members
    +        """
    +        return [Member(member, self) for member in self._client._send_request(f"network/{self.id}/member")]
    +
    +    def add_member(self, address, name=None, private_ip=None, authorized=True):
    +        """Add a member to the network
    +
    +        Args:
    +            address (str): Address of the member
    +            name (str, optional): Name of the member. Defaults to None.
    +            private_ip (str, optional): Private IP to assign. Defaults to None.
    +            authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    +
    +        Returns:
    +            Member: Added member
    +        """
    +        data = {"config": {"authorized": authorized}, "name": name}
    +        if private_ip:
    +            data["config"]["ipAssignments"] = [private_ip]
    +        member_data = self._client._send_request(f"network/{self.id}/member/{address}", method="post", data=data)
    +        return Member(member_data, self, address)
    +
    +    def update_member(self, address, name=None, private_ip=None, authorized=None):
    +        """Update a member in the network
    +
    +        Args:
    +            address (str): Address of the member
    +            name (str, optional): Name of the member. Defaults to None.
    +            private_ip (str, optional): Private IP to assign. Defaults to None.
    +            authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    +
    +        Returns:
    +            Member: Added member
    +        """
    +        return self.add_member(address, name, private_ip, authorized)
    +
    +    def get_member(self, address):
    +        """Returns member by address
    +
    +        Args:
    +            address (str): Member address
    +
    +        Returns:
    +            Member: Found member
    +        """
    +        return Member(self._client._send_request(f"network/{self.id}/member/{address}"), self, address)
    +
    +    def delete_member(self, address):
    +        """Deletes a member
    +
    +        Args:
    +            address (str): Member address
    +        """
    +        self._client._send_request(f"network/{self.id}/member/{address}", method="delete", return_json=False)
    +
    +    def __repr__(self):
    +        return f"Network({self.id})"
    +
    +

    Instance variables

    +
    +
    var name
    +
    +
    +
    + +Expand source code + +
    @property
    +def name(self):
    +    return self.raw_data["config"]["name"]
    +
    +
    +
    var routes
    +
    +
    +
    + +Expand source code + +
    @property
    +def routes(self):
    +    return self.raw_data["config"]["routes"]
    +
    +
    +
    +

    Methods

    +
    +
    +def add_member(self, address, name=None, private_ip=None, authorized=True) +
    +
    +

    Add a member to the network

    +

    Args

    +
    +
    address : str
    +
    Address of the member
    +
    name : str, optional
    +
    Name of the member. Defaults to None.
    +
    private_ip : str, optional
    +
    Private IP to assign. Defaults to None.
    +
    authorized : bool, optional
    +
    Whether to authorize the user or not. Defaults to True.
    +
    +

    Returns

    +
    +
    Member
    +
    Added member
    +
    +
    + +Expand source code + +
    def add_member(self, address, name=None, private_ip=None, authorized=True):
    +    """Add a member to the network
    +
    +    Args:
    +        address (str): Address of the member
    +        name (str, optional): Name of the member. Defaults to None.
    +        private_ip (str, optional): Private IP to assign. Defaults to None.
    +        authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    +
    +    Returns:
    +        Member: Added member
    +    """
    +    data = {"config": {"authorized": authorized}, "name": name}
    +    if private_ip:
    +        data["config"]["ipAssignments"] = [private_ip]
    +    member_data = self._client._send_request(f"network/{self.id}/member/{address}", method="post", data=data)
    +    return Member(member_data, self, address)
    +
    +
    +
    +def delete_member(self, address) +
    +
    +

    Deletes a member

    +

    Args

    +
    +
    address : str
    +
    Member address
    +
    +
    + +Expand source code + +
    def delete_member(self, address):
    +    """Deletes a member
    +
    +    Args:
    +        address (str): Member address
    +    """
    +    self._client._send_request(f"network/{self.id}/member/{address}", method="delete", return_json=False)
    +
    +
    +
    +def get_member(self, address) +
    +
    +

    Returns member by address

    +

    Args

    +
    +
    address : str
    +
    Member address
    +
    +

    Returns

    +
    +
    Member
    +
    Found member
    +
    +
    + +Expand source code + +
    def get_member(self, address):
    +    """Returns member by address
    +
    +    Args:
    +        address (str): Member address
    +
    +    Returns:
    +        Member: Found member
    +    """
    +    return Member(self._client._send_request(f"network/{self.id}/member/{address}"), self, address)
    +
    +
    +
    +def list_members(self) +
    +
    +

    List all members of +that network

    +

    Returns

    +
    +
    list
    +
    List of all members
    +
    +
    + +Expand source code + +
    def list_members(self):
    +    """List all members of  that network
    +
    +    Returns:
    +        list: List of all members
    +    """
    +    return [Member(member, self) for member in self._client._send_request(f"network/{self.id}/member")]
    +
    +
    +
    +def update_member(self, address, name=None, private_ip=None, authorized=None) +
    +
    +

    Update a member in the network

    +

    Args

    +
    +
    address : str
    +
    Address of the member
    +
    name : str, optional
    +
    Name of the member. Defaults to None.
    +
    private_ip : str, optional
    +
    Private IP to assign. Defaults to None.
    +
    authorized : bool, optional
    +
    Whether to authorize the user or not. Defaults to True.
    +
    +

    Returns

    +
    +
    Member
    +
    Added member
    +
    +
    + +Expand source code + +
    def update_member(self, address, name=None, private_ip=None, authorized=None):
    +    """Update a member in the network
    +
    +    Args:
    +        address (str): Address of the member
    +        name (str, optional): Name of the member. Defaults to None.
    +        private_ip (str, optional): Private IP to assign. Defaults to None.
    +        authorized (bool, optional): Whether to authorize the user or not. Defaults to True.
    +
    +    Returns:
    +        Member: Added member
    +    """
    +    return self.add_member(address, name, private_ip, authorized)
    +
    +
    +
    +
    +
    +class ZerotierClient +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class ZerotierClient(Client):
    +    base_url = fields.String(default="https://my.zerotier.com/api")
    +    token = fields.String()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._session = None
    +
    +    def _send_request(self, path, method="get", data=None, return_json=True):
    +        url = f"{self.base_url}/{path}"
    +        func_method = getattr(self.session, method)
    +        res = func_method(url, json=data)
    +        res.raise_for_status()
    +        if return_json:
    +            return res.json()
    +        else:
    +            return res.content
    +
    +    @property
    +    def session(self):
    +        if not self._session:
    +            if not self.token:
    +                raise j.exceptions.Value("Please set token to use the client")
    +            self._session = requests.Session()
    +            self._session.headers["Authorization"] = f"Bearer {self.token}"
    +        return self._session
    +
    +    def list_networks(self):
    +        """List networks available to user
    +
    +        Returns:
    +            list: All available networks
    +        """
    +        return [Network(network, self) for network in self._send_request("network")]
    +
    +    def get_network(self, network_id):
    +        """Get network by id
    +
    +        Args:
    +            network_id (str): Network id
    +
    +        Returns:
    +            Network: Found network
    +        """
    +        return Network(self._send_request(f"network/{network_id}"), self)
    +
    +    def create_network(self, public, target_subnet=None, name=None, auto_assign=True):
    +        """Create a new network
    +
    +        Args:
    +            public (bool): Specify if network is public or not
    +            target_subnet (str, optional): Target network to be pick assignment ips from. Defaults to None.
    +            name (str, optional): Name of the network. Defaults to None.
    +            auto_assign (bool, optional): If true auto assign addresses. Defaults to True.
    +
    +        Returns:
    +            Network: Created network
    +        """
    +        routes = None
    +        config = {"private": not public, "noAutoAssignIps": not auto_assign, "routes": routes}
    +        if target_subnet:
    +            routes.append({"target": target_subnet, "via": None})
    +            network = ipaddress.IPv4Network(target_subnet)
    +            config["ipAssignmentPools"] = [{"ipRangeStart": network[0], "ipRangeEnd": network[-1]}]
    +
    +        if name:
    +            config.update({"name": name})
    +
    +        data = {"config": config}
    +        return Network(self._send_request(f"network", data=data, method="post"), self)
    +
    +    def delete_network(self, network_id):
    +        """Deletes network
    +
    +        Args:
    +            network_id (str): Network id
    +        """
    +        self._send_request(f"network/{network_id}", method="delete", return_json=False)
    +
    +    def get_status(self):
    +        return self._send_request("status")
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var base_url
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var session
    +
    +
    +
    + +Expand source code + +
    @property
    +def session(self):
    +    if not self._session:
    +        if not self.token:
    +            raise j.exceptions.Value("Please set token to use the client")
    +        self._session = requests.Session()
    +        self._session.headers["Authorization"] = f"Bearer {self.token}"
    +    return self._session
    +
    +
    +
    var token
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def create_network(self, public, target_subnet=None, name=None, auto_assign=True) +
    +
    +

    Create a new network

    +

    Args

    +
    +
    public : bool
    +
    Specify if network is public or not
    +
    target_subnet : str, optional
    +
    Target network to be pick assignment ips from. Defaults to None.
    +
    name : str, optional
    +
    Name of the network. Defaults to None.
    +
    auto_assign : bool, optional
    +
    If true auto assign addresses. Defaults to True.
    +
    +

    Returns

    +
    +
    Network
    +
    Created network
    +
    +
    + +Expand source code + +
    def create_network(self, public, target_subnet=None, name=None, auto_assign=True):
    +    """Create a new network
    +
    +    Args:
    +        public (bool): Specify if network is public or not
    +        target_subnet (str, optional): Target network to be pick assignment ips from. Defaults to None.
    +        name (str, optional): Name of the network. Defaults to None.
    +        auto_assign (bool, optional): If true auto assign addresses. Defaults to True.
    +
    +    Returns:
    +        Network: Created network
    +    """
    +    routes = None
    +    config = {"private": not public, "noAutoAssignIps": not auto_assign, "routes": routes}
    +    if target_subnet:
    +        routes.append({"target": target_subnet, "via": None})
    +        network = ipaddress.IPv4Network(target_subnet)
    +        config["ipAssignmentPools"] = [{"ipRangeStart": network[0], "ipRangeEnd": network[-1]}]
    +
    +    if name:
    +        config.update({"name": name})
    +
    +    data = {"config": config}
    +    return Network(self._send_request(f"network", data=data, method="post"), self)
    +
    +
    +
    +def delete_network(self, network_id) +
    +
    +

    Deletes network

    +

    Args

    +
    +
    network_id : str
    +
    Network id
    +
    +
    + +Expand source code + +
    def delete_network(self, network_id):
    +    """Deletes network
    +
    +    Args:
    +        network_id (str): Network id
    +    """
    +    self._send_request(f"network/{network_id}", method="delete", return_json=False)
    +
    +
    +
    +def get_network(self, network_id) +
    +
    +

    Get network by id

    +

    Args

    +
    +
    network_id : str
    +
    Network id
    +
    +

    Returns

    +
    +
    Network
    +
    Found network
    +
    +
    + +Expand source code + +
    def get_network(self, network_id):
    +    """Get network by id
    +
    +    Args:
    +        network_id (str): Network id
    +
    +    Returns:
    +        Network: Found network
    +    """
    +    return Network(self._send_request(f"network/{network_id}"), self)
    +
    +
    +
    +def get_status(self) +
    +
    +
    +
    + +Expand source code + +
    def get_status(self):
    +    return self._send_request("status")
    +
    +
    +
    +def list_networks(self) +
    +
    +

    List networks available to user

    +

    Returns

    +
    +
    list
    +
    All available networks
    +
    +
    + +Expand source code + +
    def list_networks(self):
    +    """List networks available to user
    +
    +    Returns:
    +        list: All available networks
    +    """
    +    return [Network(network, self) for network in self._send_request("network")]
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/core/application/application.html b/docs/api/jumpscale/core/application/application.html index 75c0f7473..47e5093b3 100644 --- a/docs/api/jumpscale/core/application/application.html +++ b/docs/api/jumpscale/core/application/application.html @@ -113,4 +113,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/application/index.html b/docs/api/jumpscale/core/application/index.html index acf7b8a81..225909011 100644 --- a/docs/api/jumpscale/core/application/index.html +++ b/docs/api/jumpscale/core/application/index.html @@ -94,4 +94,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/events.html b/docs/api/jumpscale/core/base/events.html index 61c00bf88..60e01cd18 100644 --- a/docs/api/jumpscale/core/base/events.html +++ b/docs/api/jumpscale/core/base/events.html @@ -86,6 +86,7 @@

    Ancestors

    Subclasses

    @@ -186,4 +187,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/factory.html b/docs/api/jumpscale/core/base/factory.html index de98da0b4..8ba56c1be 100644 --- a/docs/api/jumpscale/core/base/factory.html +++ b/docs/api/jumpscale/core/base/factory.html @@ -1519,7 +1519,22 @@

    Ancestors

    Subclasses

    Class variables

    @@ -1866,4 +1881,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/fields.html b/docs/api/jumpscale/core/base/fields.html index afa489691..f554d23bd 100644 --- a/docs/api/jumpscale/core/base/fields.html +++ b/docs/api/jumpscale/core/base/fields.html @@ -4696,4 +4696,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/index.html b/docs/api/jumpscale/core/base/index.html index 24d3cdbf8..644d98bd3 100644 --- a/docs/api/jumpscale/core/base/index.html +++ b/docs/api/jumpscale/core/base/index.html @@ -90,4 +90,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/meta.html b/docs/api/jumpscale/core/base/meta.html index ea55a1f55..1d2a2d6f1 100644 --- a/docs/api/jumpscale/core/base/meta.html +++ b/docs/api/jumpscale/core/base/meta.html @@ -1050,10 +1050,35 @@

    Ancestors

    Subclasses

    Static methods

    @@ -1331,4 +1356,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/store/filesystem.html b/docs/api/jumpscale/core/base/store/filesystem.html index 207b7c5df..bf16c3e33 100644 --- a/docs/api/jumpscale/core/base/store/filesystem.html +++ b/docs/api/jumpscale/core/base/store/filesystem.html @@ -548,4 +548,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/store/index.html b/docs/api/jumpscale/core/base/store/index.html index 4ac45e098..6179517e0 100644 --- a/docs/api/jumpscale/core/base/store/index.html +++ b/docs/api/jumpscale/core/base/store/index.html @@ -1312,4 +1312,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/store/redis.html b/docs/api/jumpscale/core/base/store/redis.html index 55f58d0d5..0334f85d6 100644 --- a/docs/api/jumpscale/core/base/store/redis.html +++ b/docs/api/jumpscale/core/base/store/redis.html @@ -545,4 +545,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/store/serializers.html b/docs/api/jumpscale/core/base/store/serializers.html index f4e126583..8e1a3335f 100644 --- a/docs/api/jumpscale/core/base/store/serializers.html +++ b/docs/api/jumpscale/core/base/store/serializers.html @@ -193,4 +193,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/base/store/whooshfts.html b/docs/api/jumpscale/core/base/store/whooshfts.html index fd6331b5b..6f8beb309 100644 --- a/docs/api/jumpscale/core/base/store/whooshfts.html +++ b/docs/api/jumpscale/core/base/store/whooshfts.html @@ -683,4 +683,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/config/config.html b/docs/api/jumpscale/core/config/config.html index ec3a08e24..88f9667e1 100644 --- a/docs/api/jumpscale/core/config/config.html +++ b/docs/api/jumpscale/core/config/config.html @@ -735,4 +735,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/config/index.html b/docs/api/jumpscale/core/config/index.html index 6337ebfd3..9c1b18193 100644 --- a/docs/api/jumpscale/core/config/index.html +++ b/docs/api/jumpscale/core/config/index.html @@ -68,4 +68,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/db/index.html b/docs/api/jumpscale/core/db/index.html index afc092899..41d881568 100644 --- a/docs/api/jumpscale/core/db/index.html +++ b/docs/api/jumpscale/core/db/index.html @@ -106,4 +106,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/dirs/dirs.html b/docs/api/jumpscale/core/dirs/dirs.html index a910fda39..55a9f9ff5 100644 --- a/docs/api/jumpscale/core/dirs/dirs.html +++ b/docs/api/jumpscale/core/dirs/dirs.html @@ -23,37 +23,37 @@

    Module jumpscale.core.dirs.dirs

    This module defines main dirs in jumpscale to be used

    -
    JS-NG> j.core.dirs
    +
    JS-NG> j.core.dirs                                                                                                                                                        
     ExportedModule(__doc__=None, _exportedas=<class 'jumpscale.core.dirs.dirs.Dirs'>, _loaded=True, _m=<module 'jumpscale.core.dirs' from '/home/ahmed/wspace/js/js-ng/jumpscale/core/dirs/__init__.py'>)
     
    -JS-NG> j.core.dirs.BASEDIR
    +JS-NG> j.core.dirs.BASEDIR                                                                                                                                                
     '/home/ahmed/sandbox'
     
    -JS-NG> j.core.dirs.BINDIR
    +JS-NG> j.core.dirs.BINDIR                                                                                                                                                 
     '/home/ahmed/sandbox/bin'
     
    -JS-NG>
    -JS-NG> j.core.dirs.CFGDIR
    +JS-NG>                                                                                                                                                                    
    +JS-NG> j.core.dirs.CFGDIR                                                                                                                                                 
     '/home/ahmed/sandbox/cfg'
     
    -JS-NG>
    -JS-NG> j.core.dirs.CODEDIR
    +JS-NG>                                                                                                                                                                    
    +JS-NG> j.core.dirs.CODEDIR                                                                                                                                                
     '/home/ahmed/sandbox/code'
     
    -JS-NG>
    -JS-NG> j.core.dirs.HOMEDIR
    +JS-NG>                                                                                                                                                                    
    +JS-NG> j.core.dirs.HOMEDIR                                                                                                                                                
     '/home/ahmed'
     
    -JS-NG> j.core.dirs.LOGDIR
    +JS-NG> j.core.dirs.LOGDIR                                                                                                                                                 
     '/home/ahmed/sandbox/var/log'
     
    -JS-NG> j.core.dirs.TEMPLATEDIR
    +JS-NG> j.core.dirs.TEMPLATEDIR                                                                                                                                            
     '/home/ahmed/sandbox/var/templates'
     
    -JS-NG> j.core.dirs.TMPDIR
    +JS-NG> j.core.dirs.TMPDIR                                                                                                                                                 
     '/tmp/jumpscale'
     
    -JS-NG> j.core.dirs.VARDIR
    +JS-NG> j.core.dirs.VARDIR                                                                                                                                                 
     '/home/ahmed/sandbox/var'
     
    @@ -64,37 +64,37 @@

    Module jumpscale.core.dirs.dirs

    This module defines main dirs in jumpscale to be used ``` -JS-NG> j.core.dirs +JS-NG> j.core.dirs ExportedModule(__doc__=None, _exportedas=<class 'jumpscale.core.dirs.dirs.Dirs'>, _loaded=True, _m=<module 'jumpscale.core.dirs' from '/home/ahmed/wspace/js/js-ng/jumpscale/core/dirs/__init__.py'>) -JS-NG> j.core.dirs.BASEDIR +JS-NG> j.core.dirs.BASEDIR '/home/ahmed/sandbox' -JS-NG> j.core.dirs.BINDIR +JS-NG> j.core.dirs.BINDIR '/home/ahmed/sandbox/bin' -JS-NG> -JS-NG> j.core.dirs.CFGDIR +JS-NG> +JS-NG> j.core.dirs.CFGDIR '/home/ahmed/sandbox/cfg' -JS-NG> -JS-NG> j.core.dirs.CODEDIR +JS-NG> +JS-NG> j.core.dirs.CODEDIR '/home/ahmed/sandbox/code' -JS-NG> -JS-NG> j.core.dirs.HOMEDIR +JS-NG> +JS-NG> j.core.dirs.HOMEDIR '/home/ahmed' -JS-NG> j.core.dirs.LOGDIR +JS-NG> j.core.dirs.LOGDIR '/home/ahmed/sandbox/var/log' -JS-NG> j.core.dirs.TEMPLATEDIR +JS-NG> j.core.dirs.TEMPLATEDIR '/home/ahmed/sandbox/var/templates' -JS-NG> j.core.dirs.TMPDIR +JS-NG> j.core.dirs.TMPDIR '/tmp/jumpscale' -JS-NG> j.core.dirs.VARDIR +JS-NG> j.core.dirs.VARDIR '/home/ahmed/sandbox/var' ``` @@ -233,4 +233,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/dirs/index.html b/docs/api/jumpscale/core/dirs/index.html index 6c3b4be90..3aff0a778 100644 --- a/docs/api/jumpscale/core/dirs/index.html +++ b/docs/api/jumpscale/core/dirs/index.html @@ -94,4 +94,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/events/index.html b/docs/api/jumpscale/core/events/index.html index 7c0b0de7e..e7a976a2f 100644 --- a/docs/api/jumpscale/core/events/index.html +++ b/docs/api/jumpscale/core/events/index.html @@ -390,4 +390,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/exceptions/exceptions.html b/docs/api/jumpscale/core/exceptions/exceptions.html index d12753b9f..694522ac5 100644 --- a/docs/api/jumpscale/core/exceptions/exceptions.html +++ b/docs/api/jumpscale/core/exceptions/exceptions.html @@ -260,6 +260,9 @@

    Ancestors

    Subclasses

    @@ -551,4 +557,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/exceptions/index.html b/docs/api/jumpscale/core/exceptions/index.html index 12d7b6120..f709a9fd4 100644 --- a/docs/api/jumpscale/core/exceptions/index.html +++ b/docs/api/jumpscale/core/exceptions/index.html @@ -68,4 +68,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/executors/command_builder.html b/docs/api/jumpscale/core/executors/command_builder.html index c05bba0bb..9588f0d7a 100644 --- a/docs/api/jumpscale/core/executors/command_builder.html +++ b/docs/api/jumpscale/core/executors/command_builder.html @@ -151,4 +151,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/executors/index.html b/docs/api/jumpscale/core/executors/index.html index c07c57faa..0a3d35ff4 100644 --- a/docs/api/jumpscale/core/executors/index.html +++ b/docs/api/jumpscale/core/executors/index.html @@ -85,4 +85,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/executors/local.html b/docs/api/jumpscale/core/executors/local.html index e6997d667..4578b1084 100644 --- a/docs/api/jumpscale/core/executors/local.html +++ b/docs/api/jumpscale/core/executors/local.html @@ -436,4 +436,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/executors/remote.html b/docs/api/jumpscale/core/executors/remote.html index 0219a3292..6d84c62c2 100644 --- a/docs/api/jumpscale/core/executors/remote.html +++ b/docs/api/jumpscale/core/executors/remote.html @@ -328,4 +328,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/executors/tmux.html b/docs/api/jumpscale/core/executors/tmux.html index 6335060e5..5e873b604 100644 --- a/docs/api/jumpscale/core/executors/tmux.html +++ b/docs/api/jumpscale/core/executors/tmux.html @@ -156,4 +156,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/identity/index.html b/docs/api/jumpscale/core/identity/index.html new file mode 100644 index 000000000..3d750c0b9 --- /dev/null +++ b/docs/api/jumpscale/core/identity/index.html @@ -0,0 +1,1051 @@ + + + + + + +jumpscale.core.identity API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.core.identity

    +
    +
    +
    + +Expand source code + +
    import requests
    +
    +from collections import namedtuple
    +from nacl.exceptions import CryptoError
    +
    +from jumpscale.core import exceptions
    +from jumpscale.core.base import Base, StoredFactory, fields
    +from jumpscale.core.config import get_config, update_config
    +from jumpscale.core.exceptions import Input, Value
    +from jumpscale.data import serializers
    +from jumpscale.data.encryption import mnemonic, generate_mnemonic
    +from jumpscale.data.nacl import NACL
    +from jumpscale.data.idgenerator import random_int
    +from jumpscale.data.types import Email
    +from jumpscale.tools.console import ask_choice, ask_string
    +
    +
    +class Identity(Base):
    +    _tid = fields.Integer(default=-1)
    +    words = fields.Secret()
    +    email = fields.String()
    +    tname = fields.String()
    +    admins = fields.List(fields.String())
    +
    +    def __init__(
    +        self,
    +        tname=None,
    +        email=None,
    +        words=None,
    +        _tid=-1,
    +        admins=None,
    +        *args,
    +        **kwargs,
    +    ):
    +        """
    +        Get Identity
    +
    +        Requires: tname, email and words or tid and words
    +
    +        Arguments:
    +            tname (str, optional): Name eg. example.3bot
    +            email (str, optional): Email of identity
    +            words (str): Words used to secure identity
    +            admins (list of str, optional): Admins
    +
    +        Raises: NotFound incase tid is passed but does not exists
    +        Raises: Input: when params are missing
    +        """
    +        if not words:
    +            words = generate_mnemonic()
    +
    +        super().__init__(
    +            tname=tname,
    +            email=email,
    +            words=words,
    +            _tid=_tid,
    +            admins=admins,
    +            *args,
    +            **kwargs,
    +        )
    +
    +        self._nacl = None
    +        self.verify_configuration()
    +
    +    @property
    +    def nacl(self):
    +        if not self._nacl:
    +            seed = mnemonic.mnemonic_to_key(self.words.strip())
    +            self._nacl = NACL(private_key=seed)
    +        return self._nacl
    +
    +    def verify_configuration(self):
    +        """
    +        Verifies passed arguments to constructor
    +
    +        Raises: NotFound incase tid is passed but does not exists
    +        Raises: Input: when params are missing
    +        """
    +        if not self.words:
    +            raise Input("Words are mandotory for an indentity")
    +        if self._tid != -1:
    +            self.register()
    +        else:
    +            for key in ["email", "tname"]:
    +                if not getattr(self, key):
    +                    raise Value(f"Threebot {key} not configured")
    +
    +    @property
    +    def tid(self):
    +        if self._tid == -1:
    +            self.register()
    +        return self._tid
    +
    +    def register(self, host=None):
    +        # self.verify_configuration()
    +        if self.tname not in self.admins:
    +            self.admins.append(self.tname)
    +        self._tid = random_int(1, 100000000)
    +        return self._tid
    +
    +    def set_default(self):
    +        from jumpscale.loader import j
    +
    +        return j.core.identity.set_default(self.instance_name)
    +
    +
    +def get_identity():
    +    return IdentityFactory(Identity).me
    +
    +
    +RESTART_CHOICE = "Restart from the begining"
    +REENTER_CHOICE = "Re-Enter your value"
    +CHOICES = [RESTART_CHOICE, REENTER_CHOICE]
    +
    +IdentityInfo = namedtuple("IdentityInfo", ["identity", "email", "words"])
    +
    +
    +class Restart(Exception):
    +    pass
    +
    +
    +class IdentityFactory(StoredFactory):
    +    _me = None
    +
    +    def new(
    +        self,
    +        name,
    +        tname=None,
    +        email=None,
    +        words=None,
    +        tid=-1,
    +        admins=None,
    +        **kwargs,
    +    ):
    +        instance = super().new(name, tname=tname, email=email, words=words, _tid=tid, admins=admins, **kwargs)
    +        instance.save()
    +        return instance
    +
    +    @property
    +    def is_configured(self):
    +        return self._me is not None
    +
    +    @property
    +    def me(self):
    +        if not self._me:
    +            config = get_config()
    +            default = config["threebot"]["default"]
    +            if default:
    +                self.__class__._me = self.get(name=default)
    +            else:
    +                for identity in self.list_all():
    +                    self.__class__._me = self.get(identity)
    +                    break
    +                else:
    +                    raise Value("No configured identity found")
    +        return self._me
    +
    +    def set_default(self, name):
    +        config = get_config()
    +        config["threebot"]["default"] = name
    +        update_config(config)
    +        self.__class__._me = None
    +
    +    def get_user(self, tname):
    +        response = requests.get(f"https://login.threefold.me/api/users/{tname}")
    +        if response.status_code == 404:
    +            raise exceptions.NotFound(
    +                "\nThis identity does not exist in 3bot mobile app connect, Please create an idenity first using 3Bot Connect mobile Application\n"
    +            )
    +        return response.json()
    +
    +    def ask(self):
    +        """get identity information interactively"""
    +
    +        def check_email(email):
    +            # TODO: a way to check/verify email (threefold connect or openkyc?)
    +            return Email().check(email)
    +
    +        def with_error(e):
    +            """used in a loop to re-enter the value or break by raising `Restart` exception"""
    +            response = ask_choice(f"{e}, What would you like to do? ", CHOICES)
    +            if response == RESTART_CHOICE:
    +                raise Restart
    +
    +        def get_identity_info():
    +            def fill_words():
    +                return ask_string("Copy the phrase from your 3bot Connect app here: ")
    +
    +            def fill_identity():
    +                identity = ask_string("what is your threebot name (identity)? ")
    +                if "." not in identity:
    +                    identity += ".3bot"
    +                return identity
    +
    +            user = None
    +            while not user:
    +                identity = fill_identity()
    +                try:
    +                    user = self.get_user(identity)
    +                except exceptions.NotFound as e:
    +                    with_error(e)
    +
    +            while True:
    +                email = ask_string("What is the email address associated with your identity? ")
    +                if check_email(email):
    +                    break
    +                else:
    +                    with_error("This Email address is not valid")
    +
    +            print("Configured email for this identity is {}".format(email))
    +
    +            # time to do validation of words
    +            while True:
    +                words = fill_words()
    +                try:
    +                    seed = mnemonic.mnemonic_to_key(words.strip())
    +                    key = NACL(seed).get_verification_key()
    +                    if user and key != serializers.base64.decode(user["publicKey"]):
    +                        raise exceptions.Input
    +                    break
    +                except (exceptions.NotFound, exceptions.Input, CryptoError):
    +                    with_error("Seems one or more more words entered is invalid")
    +
    +            return IdentityInfo(identity, email, words)
    +
    +        while True:
    +            try:
    +                return get_identity_info()
    +            except Restart:
    +                continue
    +            except KeyboardInterrupt:
    +                break
    +
    +
    +def export_module_as():
    +    return IdentityFactory(Identity)
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    return IdentityFactory(Identity)
    +
    +
    +
    +def get_identity() +
    +
    +
    +
    + +Expand source code + +
    def get_identity():
    +    return IdentityFactory(Identity).me
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Identity +(tname=None, email=None, words=None, admins=None, *args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    Get Identity

    +

    Requires: tname, email and words or tid and words

    +

    Arguments

    +

    tname (str, optional): Name eg. example.3bot +email (str, optional): Email of identity +words (str): Words used to secure identity +admins (list of str, optional): Admins

    +

    Raises: NotFound incase tid is passed but does not exists +Raises: Input: when params are missing

    +
    + +Expand source code + +
    class Identity(Base):
    +    _tid = fields.Integer(default=-1)
    +    words = fields.Secret()
    +    email = fields.String()
    +    tname = fields.String()
    +    admins = fields.List(fields.String())
    +
    +    def __init__(
    +        self,
    +        tname=None,
    +        email=None,
    +        words=None,
    +        _tid=-1,
    +        admins=None,
    +        *args,
    +        **kwargs,
    +    ):
    +        """
    +        Get Identity
    +
    +        Requires: tname, email and words or tid and words
    +
    +        Arguments:
    +            tname (str, optional): Name eg. example.3bot
    +            email (str, optional): Email of identity
    +            words (str): Words used to secure identity
    +            admins (list of str, optional): Admins
    +
    +        Raises: NotFound incase tid is passed but does not exists
    +        Raises: Input: when params are missing
    +        """
    +        if not words:
    +            words = generate_mnemonic()
    +
    +        super().__init__(
    +            tname=tname,
    +            email=email,
    +            words=words,
    +            _tid=_tid,
    +            admins=admins,
    +            *args,
    +            **kwargs,
    +        )
    +
    +        self._nacl = None
    +        self.verify_configuration()
    +
    +    @property
    +    def nacl(self):
    +        if not self._nacl:
    +            seed = mnemonic.mnemonic_to_key(self.words.strip())
    +            self._nacl = NACL(private_key=seed)
    +        return self._nacl
    +
    +    def verify_configuration(self):
    +        """
    +        Verifies passed arguments to constructor
    +
    +        Raises: NotFound incase tid is passed but does not exists
    +        Raises: Input: when params are missing
    +        """
    +        if not self.words:
    +            raise Input("Words are mandotory for an indentity")
    +        if self._tid != -1:
    +            self.register()
    +        else:
    +            for key in ["email", "tname"]:
    +                if not getattr(self, key):
    +                    raise Value(f"Threebot {key} not configured")
    +
    +    @property
    +    def tid(self):
    +        if self._tid == -1:
    +            self.register()
    +        return self._tid
    +
    +    def register(self, host=None):
    +        # self.verify_configuration()
    +        if self.tname not in self.admins:
    +            self.admins.append(self.tname)
    +        self._tid = random_int(1, 100000000)
    +        return self._tid
    +
    +    def set_default(self):
    +        from jumpscale.loader import j
    +
    +        return j.core.identity.set_default(self.instance_name)
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var admins
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var email
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var nacl
    +
    +
    +
    + +Expand source code + +
    @property
    +def nacl(self):
    +    if not self._nacl:
    +        seed = mnemonic.mnemonic_to_key(self.words.strip())
    +        self._nacl = NACL(private_key=seed)
    +    return self._nacl
    +
    +
    +
    var tid
    +
    +
    +
    + +Expand source code + +
    @property
    +def tid(self):
    +    if self._tid == -1:
    +        self.register()
    +    return self._tid
    +
    +
    +
    var tname
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var words
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def register(self, host=None) +
    +
    +
    +
    + +Expand source code + +
    def register(self, host=None):
    +    # self.verify_configuration()
    +    if self.tname not in self.admins:
    +        self.admins.append(self.tname)
    +    self._tid = random_int(1, 100000000)
    +    return self._tid
    +
    +
    +
    +def set_default(self) +
    +
    +
    +
    + +Expand source code + +
    def set_default(self):
    +    from jumpscale.loader import j
    +
    +    return j.core.identity.set_default(self.instance_name)
    +
    +
    +
    +def verify_configuration(self) +
    +
    +

    Verifies passed arguments to constructor

    +

    Raises: NotFound incase tid is passed but does not exists +Raises: Input: when params are missing

    +
    + +Expand source code + +
    def verify_configuration(self):
    +    """
    +    Verifies passed arguments to constructor
    +
    +    Raises: NotFound incase tid is passed but does not exists
    +    Raises: Input: when params are missing
    +    """
    +    if not self.words:
    +        raise Input("Words are mandotory for an indentity")
    +    if self._tid != -1:
    +        self.register()
    +    else:
    +        for key in ["email", "tname"]:
    +            if not getattr(self, key):
    +                raise Value(f"Threebot {key} not configured")
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class IdentityFactory +(type_, name_=None, parent_instance_=None, parent_factory_=None) +
    +
    +

    Stored factories are a custom type of Factory, which uses current configured store backend +to store all instance configurations.

    +

    get a new stored factory given the type to create and store instances for.

    +

    Any factory can have a name, parent Base instance and a parent factory.

    +

    Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

    +

    Args

    +
    +
    type_ : Base
    +
    Base class type
    +
    name_ : str, optional
    +
    factory name. Defaults to None.
    +
    parent_instance_ : Base, optional
    +
    a parent Base instance. Defaults to None.
    +
    parent_factory_ : Factory, optional
    +
    a parent Factory. Defaults to None.
    +
    +
    + +Expand source code + +
    class IdentityFactory(StoredFactory):
    +    _me = None
    +
    +    def new(
    +        self,
    +        name,
    +        tname=None,
    +        email=None,
    +        words=None,
    +        tid=-1,
    +        admins=None,
    +        **kwargs,
    +    ):
    +        instance = super().new(name, tname=tname, email=email, words=words, _tid=tid, admins=admins, **kwargs)
    +        instance.save()
    +        return instance
    +
    +    @property
    +    def is_configured(self):
    +        return self._me is not None
    +
    +    @property
    +    def me(self):
    +        if not self._me:
    +            config = get_config()
    +            default = config["threebot"]["default"]
    +            if default:
    +                self.__class__._me = self.get(name=default)
    +            else:
    +                for identity in self.list_all():
    +                    self.__class__._me = self.get(identity)
    +                    break
    +                else:
    +                    raise Value("No configured identity found")
    +        return self._me
    +
    +    def set_default(self, name):
    +        config = get_config()
    +        config["threebot"]["default"] = name
    +        update_config(config)
    +        self.__class__._me = None
    +
    +    def get_user(self, tname):
    +        response = requests.get(f"https://login.threefold.me/api/users/{tname}")
    +        if response.status_code == 404:
    +            raise exceptions.NotFound(
    +                "\nThis identity does not exist in 3bot mobile app connect, Please create an idenity first using 3Bot Connect mobile Application\n"
    +            )
    +        return response.json()
    +
    +    def ask(self):
    +        """get identity information interactively"""
    +
    +        def check_email(email):
    +            # TODO: a way to check/verify email (threefold connect or openkyc?)
    +            return Email().check(email)
    +
    +        def with_error(e):
    +            """used in a loop to re-enter the value or break by raising `Restart` exception"""
    +            response = ask_choice(f"{e}, What would you like to do? ", CHOICES)
    +            if response == RESTART_CHOICE:
    +                raise Restart
    +
    +        def get_identity_info():
    +            def fill_words():
    +                return ask_string("Copy the phrase from your 3bot Connect app here: ")
    +
    +            def fill_identity():
    +                identity = ask_string("what is your threebot name (identity)? ")
    +                if "." not in identity:
    +                    identity += ".3bot"
    +                return identity
    +
    +            user = None
    +            while not user:
    +                identity = fill_identity()
    +                try:
    +                    user = self.get_user(identity)
    +                except exceptions.NotFound as e:
    +                    with_error(e)
    +
    +            while True:
    +                email = ask_string("What is the email address associated with your identity? ")
    +                if check_email(email):
    +                    break
    +                else:
    +                    with_error("This Email address is not valid")
    +
    +            print("Configured email for this identity is {}".format(email))
    +
    +            # time to do validation of words
    +            while True:
    +                words = fill_words()
    +                try:
    +                    seed = mnemonic.mnemonic_to_key(words.strip())
    +                    key = NACL(seed).get_verification_key()
    +                    if user and key != serializers.base64.decode(user["publicKey"]):
    +                        raise exceptions.Input
    +                    break
    +                except (exceptions.NotFound, exceptions.Input, CryptoError):
    +                    with_error("Seems one or more more words entered is invalid")
    +
    +            return IdentityInfo(identity, email, words)
    +
    +        while True:
    +            try:
    +                return get_identity_info()
    +            except Restart:
    +                continue
    +            except KeyboardInterrupt:
    +                break
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var is_configured
    +
    +
    +
    + +Expand source code + +
    @property
    +def is_configured(self):
    +    return self._me is not None
    +
    +
    +
    var me
    +
    +
    +
    + +Expand source code + +
    @property
    +def me(self):
    +    if not self._me:
    +        config = get_config()
    +        default = config["threebot"]["default"]
    +        if default:
    +            self.__class__._me = self.get(name=default)
    +        else:
    +            for identity in self.list_all():
    +                self.__class__._me = self.get(identity)
    +                break
    +            else:
    +                raise Value("No configured identity found")
    +    return self._me
    +
    +
    +
    +

    Methods

    +
    +
    +def ask(self) +
    +
    +

    get identity information interactively

    +
    + +Expand source code + +
    def ask(self):
    +    """get identity information interactively"""
    +
    +    def check_email(email):
    +        # TODO: a way to check/verify email (threefold connect or openkyc?)
    +        return Email().check(email)
    +
    +    def with_error(e):
    +        """used in a loop to re-enter the value or break by raising `Restart` exception"""
    +        response = ask_choice(f"{e}, What would you like to do? ", CHOICES)
    +        if response == RESTART_CHOICE:
    +            raise Restart
    +
    +    def get_identity_info():
    +        def fill_words():
    +            return ask_string("Copy the phrase from your 3bot Connect app here: ")
    +
    +        def fill_identity():
    +            identity = ask_string("what is your threebot name (identity)? ")
    +            if "." not in identity:
    +                identity += ".3bot"
    +            return identity
    +
    +        user = None
    +        while not user:
    +            identity = fill_identity()
    +            try:
    +                user = self.get_user(identity)
    +            except exceptions.NotFound as e:
    +                with_error(e)
    +
    +        while True:
    +            email = ask_string("What is the email address associated with your identity? ")
    +            if check_email(email):
    +                break
    +            else:
    +                with_error("This Email address is not valid")
    +
    +        print("Configured email for this identity is {}".format(email))
    +
    +        # time to do validation of words
    +        while True:
    +            words = fill_words()
    +            try:
    +                seed = mnemonic.mnemonic_to_key(words.strip())
    +                key = NACL(seed).get_verification_key()
    +                if user and key != serializers.base64.decode(user["publicKey"]):
    +                    raise exceptions.Input
    +                break
    +            except (exceptions.NotFound, exceptions.Input, CryptoError):
    +                with_error("Seems one or more more words entered is invalid")
    +
    +        return IdentityInfo(identity, email, words)
    +
    +    while True:
    +        try:
    +            return get_identity_info()
    +        except Restart:
    +            continue
    +        except KeyboardInterrupt:
    +            break
    +
    +
    +
    +def get_user(self, tname) +
    +
    +
    +
    + +Expand source code + +
    def get_user(self, tname):
    +    response = requests.get(f"https://login.threefold.me/api/users/{tname}")
    +    if response.status_code == 404:
    +        raise exceptions.NotFound(
    +            "\nThis identity does not exist in 3bot mobile app connect, Please create an idenity first using 3Bot Connect mobile Application\n"
    +        )
    +    return response.json()
    +
    +
    +
    +def set_default(self, name) +
    +
    +
    +
    + +Expand source code + +
    def set_default(self, name):
    +    config = get_config()
    +    config["threebot"]["default"] = name
    +    update_config(config)
    +    self.__class__._me = None
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class IdentityInfo +(identity, email, words) +
    +
    +

    IdentityInfo(identity, email, words)

    +

    Ancestors

    +
      +
    • builtins.tuple
    • +
    +

    Instance variables

    +
    +
    var email
    +
    +

    Alias for field number 1

    +
    +
    var identity
    +
    +

    Alias for field number 0

    +
    +
    var words
    +
    +

    Alias for field number 2

    +
    +
    +
    +
    +class Restart +(*args, **kwargs) +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class Restart(Exception):
    +    pass
    +
    +

    Ancestors

    +
      +
    • builtins.Exception
    • +
    • builtins.BaseException
    • +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/core/index.html b/docs/api/jumpscale/core/index.html index ed9bf88fa..82f0ddfb2 100644 --- a/docs/api/jumpscale/core/index.html +++ b/docs/api/jumpscale/core/index.html @@ -58,6 +58,10 @@

    Sub-modules

    +
    jumpscale.core.identity
    +
    +
    +
    jumpscale.core.logging
    @@ -92,6 +96,7 @@

    Index

  • jumpscale.core.events
  • jumpscale.core.exceptions
  • jumpscale.core.executors
  • +
  • jumpscale.core.identity
  • jumpscale.core.logging
  • @@ -102,4 +107,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/logging/index.html b/docs/api/jumpscale/core/logging/index.html index e2e522b01..3ab9a942f 100644 --- a/docs/api/jumpscale/core/logging/index.html +++ b/docs/api/jumpscale/core/logging/index.html @@ -145,4 +145,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/core/logging/logging.html b/docs/api/jumpscale/core/logging/logging.html index b4388ebf2..58b7a6317 100644 --- a/docs/api/jumpscale/core/logging/logging.html +++ b/docs/api/jumpscale/core/logging/logging.html @@ -1385,4 +1385,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/bcdb.html b/docs/api/jumpscale/data/bcdb/bcdb.html index c4c381847..ccfd2bcb3 100644 --- a/docs/api/jumpscale/data/bcdb/bcdb.html +++ b/docs/api/jumpscale/data/bcdb/bcdb.html @@ -39,7 +39,7 @@

    Module jumpscale.data.bcdb.bcdb

    self.indexer_set = SQLiteIndexSetClient(ns) self.indexer_text = SonicIndexTextClient(ns) self.models = { - + } self.loaded_models = { @@ -53,10 +53,10 @@

    Module jumpscale.data.bcdb.bcdb

    model = getattr(models, model_name) if isinstance(model, type) and issubclass(model, models.ModelBase): self.models[model._name] = model - + def save_obj(self, model, obj): """Saves the given objects which belongs to model in the db and update the indexes. - + Args: model (ModelObj): The model object that obj belongs to. obj (JSObjBase): The object that will be saved. @@ -75,10 +75,10 @@

    Module jumpscale.data.bcdb.bcdb

    def model_id_incr(self, model): """Increment the id counter in the model and returns the new id. Used to assign unique id for each created object. - + Args: model (ModelObj): The model object. - + Returns: int: The new unique id """ @@ -86,11 +86,11 @@

    Module jumpscale.data.bcdb.bcdb

    def get_item_by_id(self, model, id): """Gets the object in the model with the given id. - + Args: model (ModelObj): The model to be searched in. id (int): The object's id. - + Returns: JSObjBase or None: The JSObject with the given id. None if none was found. """ @@ -101,15 +101,15 @@

    Module jumpscale.data.bcdb.bcdb

    1. It searches in the redis index if key is indexed. 2. Else, It's searched for in the sqlite index if the key is indexed for range search. 3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: JSObjBase or None: The matched object (o: o.key == val). None if none matched. """ @@ -129,16 +129,16 @@

    Module jumpscale.data.bcdb.bcdb

    def get_range(self, model, key, min, max): """Searches for objects whose key lies between min and max. It tries to search for it in the index. If the key is not indexed it loops through all the objects. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -153,20 +153,20 @@

    Module jumpscale.data.bcdb.bcdb

    if obj_val >= min and obj_val <= max: result.append(obj) return result - + def get_item_from_index(self, model, key, val): """Search for objects whose key equal val. The key must be indexed for search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key == val) """ @@ -179,17 +179,17 @@

    Module jumpscale.data.bcdb.bcdb

    def get_item_from_index_set(self, model, key, min, max): """Searches for objects whose key lies between min and max. The key must be indexed for range search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for range search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -198,23 +198,23 @@

    Module jumpscale.data.bcdb.bcdb

    if not model.schema.props[key].index_key: raise RuntimeError(f"{key} is not indexed.") return [self.get_item_by_id(model, x[0]) for x in self.indexer_set.get(model, key, min, max)] - + def get_item_from_index_text(self, model, key, pattern): """Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index. - + Args: model (Modelobj): The model object in which the pattern is searched. key (str): The model property that the pattern is searched for in. pattern (str): The pattern to be searched for. - + Notes: Currently sonic server matches for some patterns and doesn't for others. Raises: RuntimeError: If the key is not defined in the model. RuntimeError: If the key is not indexed for search - + Returns: list[JSObjBase]: List of matching objects (o: o.key matches pattern). """ @@ -226,13 +226,13 @@

    Module jumpscale.data.bcdb.bcdb

    def get_model_by_name(self, model_name): """Returns a Model object given its name. - + Args: model_name (str): The name of the model. - + Raises: RuntimeError: Raised when no model exists with the given. - + Returns: ModelObj: The model object. """ @@ -270,7 +270,7 @@

    Classes

    self.indexer_set = SQLiteIndexSetClient(ns) self.indexer_text = SonicIndexTextClient(ns) self.models = { - + } self.loaded_models = { @@ -284,10 +284,10 @@

    Classes

    model = getattr(models, model_name) if isinstance(model, type) and issubclass(model, models.ModelBase): self.models[model._name] = model - + def save_obj(self, model, obj): """Saves the given objects which belongs to model in the db and update the indexes. - + Args: model (ModelObj): The model object that obj belongs to. obj (JSObjBase): The object that will be saved. @@ -306,10 +306,10 @@

    Classes

    def model_id_incr(self, model): """Increment the id counter in the model and returns the new id. Used to assign unique id for each created object. - + Args: model (ModelObj): The model object. - + Returns: int: The new unique id """ @@ -317,11 +317,11 @@

    Classes

    def get_item_by_id(self, model, id): """Gets the object in the model with the given id. - + Args: model (ModelObj): The model to be searched in. id (int): The object's id. - + Returns: JSObjBase or None: The JSObject with the given id. None if none was found. """ @@ -332,15 +332,15 @@

    Classes

    1. It searches in the redis index if key is indexed. 2. Else, It's searched for in the sqlite index if the key is indexed for range search. 3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: JSObjBase or None: The matched object (o: o.key == val). None if none matched. """ @@ -360,16 +360,16 @@

    Classes

    def get_range(self, model, key, min, max): """Searches for objects whose key lies between min and max. It tries to search for it in the index. If the key is not indexed it loops through all the objects. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -384,20 +384,20 @@

    Classes

    if obj_val >= min and obj_val <= max: result.append(obj) return result - + def get_item_from_index(self, model, key, val): """Search for objects whose key equal val. The key must be indexed for search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key == val) """ @@ -410,17 +410,17 @@

    Classes

    def get_item_from_index_set(self, model, key, min, max): """Searches for objects whose key lies between min and max. The key must be indexed for range search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for range search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -429,23 +429,23 @@

    Classes

    if not model.schema.props[key].index_key: raise RuntimeError(f"{key} is not indexed.") return [self.get_item_by_id(model, x[0]) for x in self.indexer_set.get(model, key, min, max)] - + def get_item_from_index_text(self, model, key, pattern): """Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index. - + Args: model (Modelobj): The model object in which the pattern is searched. key (str): The model property that the pattern is searched for in. pattern (str): The pattern to be searched for. - + Notes: Currently sonic server matches for some patterns and doesn't for others. Raises: RuntimeError: If the key is not defined in the model. RuntimeError: If the key is not indexed for search - + Returns: list[JSObjBase]: List of matching objects (o: o.key matches pattern). """ @@ -457,13 +457,13 @@

    Classes

    def get_model_by_name(self, model_name): """Returns a Model object given its name. - + Args: model_name (str): The name of the model. - + Raises: RuntimeError: Raised when no model exists with the given. - + Returns: ModelObj: The model object. """ @@ -528,15 +528,15 @@

    Returns

    1. It searches in the redis index if key is indexed. 2. Else, It's searched for in the sqlite index if the key is indexed for range search. 3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: JSObjBase or None: The matched object (o: o.key == val). None if none matched. """ @@ -577,11 +577,11 @@

    Returns

    def get_item_by_id(self, model, id):
         """Gets the object in the model with the given id.
    -
    +    
         Args:
             model (ModelObj): The model to be searched in.
             id (int): The object's id.
    -
    +    
         Returns:
             JSObjBase or None: The JSObject with the given id. None if none was found.
         """
    @@ -620,16 +620,16 @@ 

    Returns

    def get_item_from_index(self, model, key, val):
         """Search for objects whose key equal val. The key must be indexed for search.
    -
    +    
         Args:
             model (ModelObj): The model in which the key is searched for.
             key (str): The model property that is checked for.
             val (value): The value.
    -
    +    
         Raises:
             RuntimeError: If the key is not a part of the schema.
             RuntimeError: If the key is not indexed for search.
    -
    +    
         Returns:
             List[JSObjBase]: A list of matched objects (o: o.key == val)
         """
    @@ -675,17 +675,17 @@ 

    Returns

    def get_item_from_index_set(self, model, key, min, max):
         """Searches for objects whose key lies between min and max. The key must be indexed for range search.
    -
    +    
         Args:
             model (ModelObj): The model in which the key is searched for.
             key (str): The model property that is checked for.
             min (value): The minimum.
             max (value): The maximum.
    -
    +    
         Raises:
             RuntimeError: If the key is not a part of the schema.
             RuntimeError: If the key is not indexed for range search.
    -
    +    
         Returns:
             List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max)
         """
    @@ -730,19 +730,19 @@ 

    Returns

    def get_item_from_index_text(self, model, key, pattern):
         """Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index.
    -
    +    
         Args:
             model (Modelobj): The model object in which the pattern is searched.
             key (str): The model property that the pattern is searched for in.
             pattern (str): The pattern to be searched for.
    -
    +    
         Notes:
             Currently sonic server matches for some patterns and doesn't for others.
     
         Raises:
             RuntimeError: If the key is not defined in the model.
             RuntimeError: If the key is not indexed for search
    -
    +    
         Returns:
             list[JSObjBase]: List of matching objects (o: o.key matches pattern).
         """
    @@ -779,13 +779,13 @@ 

    Returns

    def get_model_by_name(self, model_name):
         """Returns a Model object given its name.
    -
    +    
         Args:
             model_name (str): The name of the model.
    -
    +    
         Raises:
             RuntimeError: Raised when no model exists with the given.
    -
    +    
         Returns:
             ModelObj: The model object.
         """
    @@ -830,16 +830,16 @@ 

    Returns

    def get_range(self, model, key, min, max):
         """Searches for objects whose key lies between min and max.
         It tries to search for it in the index. If the key is not indexed it loops through all the objects.
    -
    +    
         Args:
             model (ModelObj): The model in which the key is searched for.
             key (str): The model property that is checked for.
             min (value): The minimum.
             max (value): The maximum.
    -
    +    
         Raises:
             RuntimeError: If the key is not a part of the schema.
    -
    +    
         Returns:
             List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max)
         """
    @@ -879,10 +879,10 @@ 

    Returns

    def model_id_incr(self, model):
         """Increment the id counter in the model and returns the new id.
         Used to assign unique id for each created object.
    -
    +    
         Args:
             model (ModelObj): The model object.
    -
    +    
         Returns:
             int: The new unique id
         """
    @@ -907,7 +907,7 @@ 

    Args

    def save_obj(self, model, obj):
         """Saves the given objects which belongs to model in the db and update the indexes.
    -
    +    
         Args:
             model (ModelObj): The model object that obj belongs to.
             obj (JSObjBase): The object that will be saved.
    @@ -966,4 +966,4 @@ 

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/clients.html b/docs/api/jumpscale/data/bcdb/clients.html index bbdb9fdcf..ff18b1525 100644 --- a/docs/api/jumpscale/data/bcdb/clients.html +++ b/docs/api/jumpscale/data/bcdb/clients.html @@ -627,4 +627,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/dumpsql.html b/docs/api/jumpscale/data/bcdb/dumpsql.html index 81693cd81..36a077b9d 100644 --- a/docs/api/jumpscale/data/bcdb/dumpsql.html +++ b/docs/api/jumpscale/data/bcdb/dumpsql.html @@ -98,4 +98,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/flush.html b/docs/api/jumpscale/data/bcdb/flush.html index 68faff70d..03bc159a4 100644 --- a/docs/api/jumpscale/data/bcdb/flush.html +++ b/docs/api/jumpscale/data/bcdb/flush.html @@ -112,4 +112,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/index.html b/docs/api/jumpscale/data/bcdb/index.html index 350c2fc9a..9e8a2eff8 100644 --- a/docs/api/jumpscale/data/bcdb/index.html +++ b/docs/api/jumpscale/data/bcdb/index.html @@ -93,4 +93,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/interfaces.html b/docs/api/jumpscale/data/bcdb/interfaces.html index 15a40eec2..00f8e0779 100644 --- a/docs/api/jumpscale/data/bcdb/interfaces.html +++ b/docs/api/jumpscale/data/bcdb/interfaces.html @@ -67,7 +67,7 @@

    Module jumpscale.data.bcdb.interfaces

    pass def get(self, model, index_prop, pattern): - pass + pass class SerializerInterface: def loads(self, model, s): @@ -442,4 +442,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/base.html b/docs/api/jumpscale/data/bcdb/models/base.html index 4d97451fa..d0a706c65 100644 --- a/docs/api/jumpscale/data/bcdb/models/base.html +++ b/docs/api/jumpscale/data/bcdb/models/base.html @@ -816,4 +816,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/db_model.html b/docs/api/jumpscale/data/bcdb/models/db_model.html index bed186dd2..01bca65b4 100644 --- a/docs/api/jumpscale/data/bcdb/models/db_model.html +++ b/docs/api/jumpscale/data/bcdb/models/db_model.html @@ -111,4 +111,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/emplyee_model.html b/docs/api/jumpscale/data/bcdb/models/emplyee_model.html index 9c2fc5fef..0cb3cf4b8 100644 --- a/docs/api/jumpscale/data/bcdb/models/emplyee_model.html +++ b/docs/api/jumpscale/data/bcdb/models/emplyee_model.html @@ -113,4 +113,4 @@

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/index.html b/docs/api/jumpscale/data/bcdb/models/index.html index 0d4f460b7..52eef71e2 100644 --- a/docs/api/jumpscale/data/bcdb/models/index.html +++ b/docs/api/jumpscale/data/bcdb/models/index.html @@ -142,4 +142,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/model_model.html b/docs/api/jumpscale/data/bcdb/models/model_model.html index 32ee6b4b0..93764b6b4 100644 --- a/docs/api/jumpscale/data/bcdb/models/model_model.html +++ b/docs/api/jumpscale/data/bcdb/models/model_model.html @@ -114,4 +114,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/post_model.html b/docs/api/jumpscale/data/bcdb/models/post_model.html index 06bc8d79f..c7d771f46 100644 --- a/docs/api/jumpscale/data/bcdb/models/post_model.html +++ b/docs/api/jumpscale/data/bcdb/models/post_model.html @@ -111,4 +111,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/proj_model.html b/docs/api/jumpscale/data/bcdb/models/proj_model.html index 1bbc85440..3b60a5b64 100644 --- a/docs/api/jumpscale/data/bcdb/models/proj_model.html +++ b/docs/api/jumpscale/data/bcdb/models/proj_model.html @@ -113,4 +113,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/test_model.html b/docs/api/jumpscale/data/bcdb/models/test_model.html index c72769c64..94e5bd8ef 100644 --- a/docs/api/jumpscale/data/bcdb/models/test_model.html +++ b/docs/api/jumpscale/data/bcdb/models/test_model.html @@ -109,4 +109,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/bcdb/models/user_model.html b/docs/api/jumpscale/data/bcdb/models/user_model.html index 4e47391c0..dd600cb73 100644 --- a/docs/api/jumpscale/data/bcdb/models/user_model.html +++ b/docs/api/jumpscale/data/bcdb/models/user_model.html @@ -111,4 +111,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/cache/index.html b/docs/api/jumpscale/data/cache/index.html index d404e754e..23a2ecad5 100644 --- a/docs/api/jumpscale/data/cache/index.html +++ b/docs/api/jumpscale/data/cache/index.html @@ -56,4 +56,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/countries/index.html b/docs/api/jumpscale/data/countries/index.html new file mode 100644 index 000000000..4edbdfec3 --- /dev/null +++ b/docs/api/jumpscale/data/countries/index.html @@ -0,0 +1,141 @@ + + + + + + +jumpscale.data.countries API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.data.countries

    +
    +
    +

    This module helps picking a random country or even listing all countries.

    +
    JS-NG> j.data.countries.names()                                                        
    +['Aruba', 'Afghanistan', 'Angola', 'Anguilla', 'Åland Islands', 'Albania', 'Andorra', '
    +United Arab Emirates', 'Argentina', 'Armenia', 'American Samoa', 'Antarctica', 'French 
    +Southern Territories', 'Antigua and Barbuda', 'Australia', 'Austria', 'Azerbaijan', 'Bu
    +rundi', 'Belgium', 'Benin', 'Bonaire, Sint Eustatius and Saba', 'Burkina Faso', 'Bangla
    +desh', 'Bulgaria', 'Bahrain', 'Bahamas', 'Bosnia and Herzegovina', 'Saint Barthélemy', 
    +'Belarus', 'Belize', 'Bermuda', 'Bolivia, Plurinational State of', 'Brazil', 'Barbados', 'Brunei Darussalam', 'Bhutan', 'Bouvet Island', 'Botswana', 'Central African Republic', 'Canada', 'Cocos (Keeling) Islands', 'Switzerland', 'Chile', 'China', "Côte d'Ivoire", 'Cameroon', 'Congo, The Democratic Republic of the', 'Congo', 'Cook Islands', 'Colombia', 'Comoros', 'Cabo Verde', 'Costa Rica', 'Cuba', 'Curaçao', 'Christmas Island', 'Cayman Islands', 'Cyprus', 'Czechia', 'Germany', 'Djibouti', 'Dominica', 'Denmark', 'Dominican Republic', 'Algeria', 'Ecuador', 'Egypt', 'Eritrea', 'Western Sahara', 'Spain', 'Estonia', 'Ethiopia', 'Finland', 'Fiji', 'Falkland Islands (Malvinas)', 'France', 'Faroe Islands', 'Micronesia, Federated States of', 'Gabon', 'United Kingdom', 'Georgia', 'Guernsey', 'Ghana', 'Gibraltar', 'Guinea', 'Guadeloupe', 'Gambia', 'Guinea-Bissau', 'Equatorial Guinea', 'Greece', 'Grenada', 'Greenland', 'Guatemala', 'French Guiana', 'Guam', 'Guyana', 'Hong Kong', 'Heard Island and McDonald Islands', 'Honduras', 'Croatia', 'Haiti', 'Hungary', 'Indonesia', 'Isle of Man', 'India', 'British Indian Ocean Territory', 'Ireland', 'Iran, Islamic Republic of', 'Iraq', 'Iceland', 'Israel', 'Italy', 'Jamaica', 'Jersey', 'Jordan', 'Japan', 'Kazakhstan', 'Kenya', 'Kyrgyzstan', 'Cambodia', 'Kiribati', 'Saint Kitts and Nevis', 'Korea, Republic of', 'Kuwait', "Lao People's Democratic Republic", 'Lebanon', 'Liberia', 'Libya', 'Saint Lucia', 'Liechtenstein', 'Sri Lanka', 'Lesotho', 'Lithuania', 'Luxembourg', 'Latvia', 'Macao', 'Saint Martin (French part)', 'Morocco', 'Monaco', 'Moldova, Republic of', 'Madagascar', 'Maldives', 'Mexico', 'Marshall Islands', 'North Macedonia', 'Mali', 'Malta', 'Myanmar', 'Montenegro', 'Mongolia', 'Northern Mariana Islands', 'Mozambique', 'Mauritania', 'Montserrat', 'Martinique', 'Mauritius', 'Malawi', 'Malaysia', 'Mayotte', 'Namibia', 'New Caledonia', 'Niger', 'Norfolk Island', 'Nigeria', 'Nicaragua', 'Niue', 'Netherlands', 'Norway', 'Nepal', 'Nauru', 'New Zealand', 'Oman', 'Pakistan', 'Panama', 'Pitcairn', 'Peru', 'Philippines', 'Palau', 'Papua New Guinea', 'Poland', 'Puerto Rico', "Korea, Democratic People's Republic of", 'Portugal', 'Paraguay', 'Palestine, State of', 'French Polynesia', 'Qatar', 'Réunion', 'Romania', 'Russian Federation', 'Rwanda', 'Saudi Arabia', 'Sudan', 'Senegal', 'Singapore', 'South Georgia and the South Sandwich Islands', 'Saint Helena, Ascension and Tristan da Cunha', 'Svalbard and Jan Mayen', 'Solomon Islands', 'Sierra Leone', 'El Salvador', 'San Marino', 'Somalia', 'Saint Pierre and Miquelon', 'Serbia', 'South Sudan', 'Sao Tome and Principe', 'Suriname', 'Slovakia', 'Slovenia', 'Sweden', 'Eswatini', 'Sint Maarten (Dutch part)', 'Seychelles', 'Syrian Arab Republic', 'Turks and Caicos Islands', 'Chad', 'Togo', 'Thailand', 'Tajikistan', 'Tokelau', 'Turkmenistan', 'Timor-Leste', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Tuvalu', 'Taiwan, Province of China', 'Tanzania, United Republic of', 'Uganda', 'Ukraine', 'United States Minor Outlying Islands', 'Uruguay', 'United States', 'Uzbekistan', 'Holy See (Vatican City State)', 'Saint Vincent and the Grenadines', 'Venezuela, Bolivarian Republic of', 'Virgin Islands, British', 'Virgin Islands, U.S.', 'Viet Nam', 'Vanuatu', 'Wallis and Futuna', 'Samoa', 'Yemen', 'South Africa', 'Zambia', 'Zimbabwe']
    +
    +JS-NG> j.data.countries.random_country()                                               
    +'Seychelles'
    +
    +JS-NG> j.data.countries.random_country()                                               
    +'Malawi'
    +
    +
    + +Expand source code + +
    """This module helps picking a random country or even listing all countries.
    +```
    +JS-NG> j.data.countries.names()                                                        
    +['Aruba', 'Afghanistan', 'Angola', 'Anguilla', 'Åland Islands', 'Albania', 'Andorra', '
    +United Arab Emirates', 'Argentina', 'Armenia', 'American Samoa', 'Antarctica', 'French 
    +Southern Territories', 'Antigua and Barbuda', 'Australia', 'Austria', 'Azerbaijan', 'Bu
    +rundi', 'Belgium', 'Benin', 'Bonaire, Sint Eustatius and Saba', 'Burkina Faso', 'Bangla
    +desh', 'Bulgaria', 'Bahrain', 'Bahamas', 'Bosnia and Herzegovina', 'Saint Barthélemy', 
    +'Belarus', 'Belize', 'Bermuda', 'Bolivia, Plurinational State of', 'Brazil', 'Barbados', 'Brunei Darussalam', 'Bhutan', 'Bouvet Island', 'Botswana', 'Central African Republic', 'Canada', 'Cocos (Keeling) Islands', 'Switzerland', 'Chile', 'China', "Côte d'Ivoire", 'Cameroon', 'Congo, The Democratic Republic of the', 'Congo', 'Cook Islands', 'Colombia', 'Comoros', 'Cabo Verde', 'Costa Rica', 'Cuba', 'Curaçao', 'Christmas Island', 'Cayman Islands', 'Cyprus', 'Czechia', 'Germany', 'Djibouti', 'Dominica', 'Denmark', 'Dominican Republic', 'Algeria', 'Ecuador', 'Egypt', 'Eritrea', 'Western Sahara', 'Spain', 'Estonia', 'Ethiopia', 'Finland', 'Fiji', 'Falkland Islands (Malvinas)', 'France', 'Faroe Islands', 'Micronesia, Federated States of', 'Gabon', 'United Kingdom', 'Georgia', 'Guernsey', 'Ghana', 'Gibraltar', 'Guinea', 'Guadeloupe', 'Gambia', 'Guinea-Bissau', 'Equatorial Guinea', 'Greece', 'Grenada', 'Greenland', 'Guatemala', 'French Guiana', 'Guam', 'Guyana', 'Hong Kong', 'Heard Island and McDonald Islands', 'Honduras', 'Croatia', 'Haiti', 'Hungary', 'Indonesia', 'Isle of Man', 'India', 'British Indian Ocean Territory', 'Ireland', 'Iran, Islamic Republic of', 'Iraq', 'Iceland', 'Israel', 'Italy', 'Jamaica', 'Jersey', 'Jordan', 'Japan', 'Kazakhstan', 'Kenya', 'Kyrgyzstan', 'Cambodia', 'Kiribati', 'Saint Kitts and Nevis', 'Korea, Republic of', 'Kuwait', "Lao People's Democratic Republic", 'Lebanon', 'Liberia', 'Libya', 'Saint Lucia', 'Liechtenstein', 'Sri Lanka', 'Lesotho', 'Lithuania', 'Luxembourg', 'Latvia', 'Macao', 'Saint Martin (French part)', 'Morocco', 'Monaco', 'Moldova, Republic of', 'Madagascar', 'Maldives', 'Mexico', 'Marshall Islands', 'North Macedonia', 'Mali', 'Malta', 'Myanmar', 'Montenegro', 'Mongolia', 'Northern Mariana Islands', 'Mozambique', 'Mauritania', 'Montserrat', 'Martinique', 'Mauritius', 'Malawi', 'Malaysia', 'Mayotte', 'Namibia', 'New Caledonia', 'Niger', 'Norfolk Island', 'Nigeria', 'Nicaragua', 'Niue', 'Netherlands', 'Norway', 'Nepal', 'Nauru', 'New Zealand', 'Oman', 'Pakistan', 'Panama', 'Pitcairn', 'Peru', 'Philippines', 'Palau', 'Papua New Guinea', 'Poland', 'Puerto Rico', "Korea, Democratic People's Republic of", 'Portugal', 'Paraguay', 'Palestine, State of', 'French Polynesia', 'Qatar', 'Réunion', 'Romania', 'Russian Federation', 'Rwanda', 'Saudi Arabia', 'Sudan', 'Senegal', 'Singapore', 'South Georgia and the South Sandwich Islands', 'Saint Helena, Ascension and Tristan da Cunha', 'Svalbard and Jan Mayen', 'Solomon Islands', 'Sierra Leone', 'El Salvador', 'San Marino', 'Somalia', 'Saint Pierre and Miquelon', 'Serbia', 'South Sudan', 'Sao Tome and Principe', 'Suriname', 'Slovakia', 'Slovenia', 'Sweden', 'Eswatini', 'Sint Maarten (Dutch part)', 'Seychelles', 'Syrian Arab Republic', 'Turks and Caicos Islands', 'Chad', 'Togo', 'Thailand', 'Tajikistan', 'Tokelau', 'Turkmenistan', 'Timor-Leste', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Tuvalu', 'Taiwan, Province of China', 'Tanzania, United Republic of', 'Uganda', 'Ukraine', 'United States Minor Outlying Islands', 'Uruguay', 'United States', 'Uzbekistan', 'Holy See (Vatican City State)', 'Saint Vincent and the Grenadines', 'Venezuela, Bolivarian Republic of', 'Virgin Islands, British', 'Virgin Islands, U.S.', 'Viet Nam', 'Vanuatu', 'Wallis and Futuna', 'Samoa', 'Yemen', 'South Africa', 'Zambia', 'Zimbabwe']
    +
    +JS-NG> j.data.countries.random_country()                                               
    +'Seychelles'
    +
    +JS-NG> j.data.countries.random_country()                                               
    +'Malawi'
    +```
    +"""
    +import pycountry
    +import random
    +
    +
    +def names():
    +    """Generates list of country names."""
    +    return [country.name for country in pycountry.countries]
    +
    +
    +def random_country():
    +    """Generates a random country name"""
    +    return random.choice(names())
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def names() +
    +
    +

    Generates list of country names.

    +
    + +Expand source code + +
    def names():
    +    """Generates list of country names."""
    +    return [country.name for country in pycountry.countries]
    +
    +
    +
    +def random_country() +
    +
    +

    Generates a random country name

    +
    + +Expand source code + +
    def random_country():
    +    """Generates a random country name"""
    +    return random.choice(names())
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/data/encryption/exceptions.html b/docs/api/jumpscale/data/encryption/exceptions.html index b6623ad09..0401cf231 100644 --- a/docs/api/jumpscale/data/encryption/exceptions.html +++ b/docs/api/jumpscale/data/encryption/exceptions.html @@ -90,4 +90,4 @@

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/encryption/index.html b/docs/api/jumpscale/data/encryption/index.html index 6843c281d..f5b7abac4 100644 --- a/docs/api/jumpscale/data/encryption/index.html +++ b/docs/api/jumpscale/data/encryption/index.html @@ -78,4 +78,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/encryption/mnemonic.html b/docs/api/jumpscale/data/encryption/mnemonic.html index 4df6dbfe8..cb33dd163 100644 --- a/docs/api/jumpscale/data/encryption/mnemonic.html +++ b/docs/api/jumpscale/data/encryption/mnemonic.html @@ -426,4 +426,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/encryption/wordlist.html b/docs/api/jumpscale/data/encryption/wordlist.html index bd637df3a..6844536ce 100644 --- a/docs/api/jumpscale/data/encryption/wordlist.html +++ b/docs/api/jumpscale/data/encryption/wordlist.html @@ -2105,4 +2105,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/fake/index.html b/docs/api/jumpscale/data/fake/index.html index 821b48ab9..13dd6481b 100644 --- a/docs/api/jumpscale/data/fake/index.html +++ b/docs/api/jumpscale/data/fake/index.html @@ -84,4 +84,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/hash/hash.html b/docs/api/jumpscale/data/hash/hash.html index 70471db8a..57f6eeae0 100644 --- a/docs/api/jumpscale/data/hash/hash.html +++ b/docs/api/jumpscale/data/hash/hash.html @@ -23,20 +23,20 @@

    Module jumpscale.data.hash.hash

    This module helps with everything related to hashing strings, bytes with popular algorithms like, md5, sha256, sha384, sha512, blake2

    -
    JS-NG> j.data.hash.md5("abc")
    +
    JS-NG> j.data.hash.md5("abc")                                                                       
     '900150983cd24fb0d6963f7d28e17f72'
     
    -JS-NG> j.data.hash.sha1("abc")
    +JS-NG> j.data.hash.sha1("abc")                                                                      
     'a9993e364706816aba3e25717850c26c9cd0d89d'
     
    -JS-NG> j.data.hash.sha224("abc")
    +JS-NG> j.data.hash.sha224("abc")                                                                    
     '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'
     
    -JS-NG> j.data.hash.sha512("abc")
    +JS-NG> j.data.hash.sha512("abc")                                                                    
     'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454
     d4423643ce80e2a9ac94fa54ca49f'
     
    -JS-NG> j.data.hash.blake2s("abc")
    +JS-NG> j.data.hash.blake2s("abc")                                                                   
     '508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982'
     
     JS-NG>
    @@ -48,24 +48,24 @@ 

    Module jumpscale.data.hash.hash

    """This module helps with everything related to hashing strings, bytes with popular algorithms like, md5, sha256, sha384, sha512, blake2
     
     ```
    -JS-NG> j.data.hash.md5("abc")
    +JS-NG> j.data.hash.md5("abc")                                                                       
     '900150983cd24fb0d6963f7d28e17f72'
     
    -JS-NG> j.data.hash.sha1("abc")
    +JS-NG> j.data.hash.sha1("abc")                                                                      
     'a9993e364706816aba3e25717850c26c9cd0d89d'
     
    -JS-NG> j.data.hash.sha224("abc")
    +JS-NG> j.data.hash.sha224("abc")                                                                    
     '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'
     
    -JS-NG> j.data.hash.sha512("abc")
    +JS-NG> j.data.hash.sha512("abc")                                                                    
     'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454
     d4423643ce80e2a9ac94fa54ca49f'
     
    -JS-NG> j.data.hash.blake2s("abc")
    +JS-NG> j.data.hash.blake2s("abc")                                                                   
     '508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982'
     
     JS-NG>
    -```
    +```  
     """
     
     import hashlib
    @@ -1034,4 +1034,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/hash/index.html b/docs/api/jumpscale/data/hash/index.html index 4f09762a4..30d1bd4f8 100644 --- a/docs/api/jumpscale/data/hash/index.html +++ b/docs/api/jumpscale/data/hash/index.html @@ -68,4 +68,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/idgenerator/idgenerator.html b/docs/api/jumpscale/data/idgenerator/idgenerator.html index e8bd397c4..4c65f79c8 100644 --- a/docs/api/jumpscale/data/idgenerator/idgenerator.html +++ b/docs/api/jumpscale/data/idgenerator/idgenerator.html @@ -23,19 +23,19 @@

    Module jumpscale.data.idgenerator.idgenerator

    idgenerator module helps with generating ids, guids, integers, chars, passwords, capnp id, a choice of a sequence

    -
    JS-NG> j.data.idgenerator.guid()
    +
    JS-NG> j.data.idgenerator.guid()                                                                    
     'c1d14970-f17f-49a3-aa85-f722013ee448'
     
    -JS-NG> j.data.idgenerator.password(5)
    +JS-NG> j.data.idgenerator.password(5)                                                               
     'b6~Sl'
     
    -JS-NG> j.data.idgenerator.capnp_id()
    +JS-NG> j.data.idgenerator.capnp_id()                                                                
     '0xa414b890b73d0940'
     
    -JS-NG> j.data.idgenerator.random_int()
    +JS-NG> j.data.idgenerator.random_int()                                                              
     7
     
    -JS-NG> j.data.idgenerator.random_int(0, 5)
    +JS-NG> j.data.idgenerator.random_int(0, 5)                                                          
     2
     
    @@ -45,19 +45,19 @@

    Module jumpscale.data.idgenerator.idgenerator

    """idgenerator module helps with generating ids, guids, integers, chars, passwords, capnp id, a choice of a sequence ``` -JS-NG> j.data.idgenerator.guid() +JS-NG> j.data.idgenerator.guid() 'c1d14970-f17f-49a3-aa85-f722013ee448' -JS-NG> j.data.idgenerator.password(5) +JS-NG> j.data.idgenerator.password(5) 'b6~Sl' -JS-NG> j.data.idgenerator.capnp_id() +JS-NG> j.data.idgenerator.capnp_id() '0xa414b890b73d0940' -JS-NG> j.data.idgenerator.random_int() +JS-NG> j.data.idgenerator.random_int() 7 -JS-NG> j.data.idgenerator.random_int(0, 5) +JS-NG> j.data.idgenerator.random_int(0, 5) 2 ``` """ @@ -391,4 +391,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/idgenerator/index.html b/docs/api/jumpscale/data/idgenerator/index.html index 0f6bf97c4..25632f330 100644 --- a/docs/api/jumpscale/data/idgenerator/index.html +++ b/docs/api/jumpscale/data/idgenerator/index.html @@ -68,4 +68,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/index.html b/docs/api/jumpscale/data/index.html index a68fa6036..da9c858a1 100644 --- a/docs/api/jumpscale/data/index.html +++ b/docs/api/jumpscale/data/index.html @@ -34,6 +34,12 @@

    Sub-modules

    +
    jumpscale.data.countries
    +
    +

    This module helps picking a random country or even listing all countries. +``` +JS-NG> j.data.countries.names() …

    +
    jumpscale.data.encryption
    @@ -92,6 +98,10 @@

    Sub-modules

    Time helpers based on arrow …

    +
    jumpscale.data.treemanager
    +
    +
    +
    jumpscale.data.types
    @@ -120,6 +130,7 @@

    Index

    @@ -144,4 +156,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/inifile/index.html b/docs/api/jumpscale/data/inifile/index.html index 8cdcbcbc9..afb84ac5c 100644 --- a/docs/api/jumpscale/data/inifile/index.html +++ b/docs/api/jumpscale/data/inifile/index.html @@ -68,4 +68,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/inifile/inifile.html b/docs/api/jumpscale/data/inifile/inifile.html index 914549b2a..e89c0adc1 100644 --- a/docs/api/jumpscale/data/inifile/inifile.html +++ b/docs/api/jumpscale/data/inifile/inifile.html @@ -673,4 +673,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/nacl/index.html b/docs/api/jumpscale/data/nacl/index.html index b163331b7..ab7205edb 100644 --- a/docs/api/jumpscale/data/nacl/index.html +++ b/docs/api/jumpscale/data/nacl/index.html @@ -125,4 +125,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/nacl/jsnacl.html b/docs/api/jumpscale/data/nacl/jsnacl.html index 36232fe5d..a4c12fc81 100644 --- a/docs/api/jumpscale/data/nacl/jsnacl.html +++ b/docs/api/jumpscale/data/nacl/jsnacl.html @@ -797,4 +797,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/platform/index.html b/docs/api/jumpscale/data/platform/index.html index 0b7e62557..aee3a075d 100644 --- a/docs/api/jumpscale/data/platform/index.html +++ b/docs/api/jumpscale/data/platform/index.html @@ -699,4 +699,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/random_names/index.html b/docs/api/jumpscale/data/random_names/index.html index fc560f5e3..fc36cedaf 100644 --- a/docs/api/jumpscale/data/random_names/index.html +++ b/docs/api/jumpscale/data/random_names/index.html @@ -23,10 +23,10 @@

    Module jumpscale.data.random_names

    This module helps genearting random names, mainly for container names.

    -
    JS-NG> j.data.random_names.random_name()
    +
    JS-NG> j.data.random_names.random_name()                                                            
     'loving_borg'
     
    -JS-NG> j.data.random_names.random_name()
    +JS-NG> j.data.random_names.random_name()                                                            
     'quirky_wiles'
     
    @@ -36,10 +36,10 @@

    Module jumpscale.data.random_names

    """This module helps genearting random names, mainly for container names.
     
     ```
    -JS-NG> j.data.random_names.random_name()
    +JS-NG> j.data.random_names.random_name()                                                            
     'loving_borg'
     
    -JS-NG> j.data.random_names.random_name()
    +JS-NG> j.data.random_names.random_name()                                                            
     'quirky_wiles'
     ```
     
    @@ -273,4 +273,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/schema/index.html b/docs/api/jumpscale/data/schema/index.html index 72936a644..3d37882b2 100644 --- a/docs/api/jumpscale/data/schema/index.html +++ b/docs/api/jumpscale/data/schema/index.html @@ -68,4 +68,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/schema/schema.html b/docs/api/jumpscale/data/schema/schema.html index 772a2448d..d9cc26183 100644 --- a/docs/api/jumpscale/data/schema/schema.html +++ b/docs/api/jumpscale/data/schema/schema.html @@ -605,4 +605,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/base64.html b/docs/api/jumpscale/data/serializers/base64.html index 8ca88ae5c..5da332a47 100644 --- a/docs/api/jumpscale/data/serializers/base64.html +++ b/docs/api/jumpscale/data/serializers/base64.html @@ -154,4 +154,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/dill.html b/docs/api/jumpscale/data/serializers/dill.html index ed9d9753a..48f309d89 100644 --- a/docs/api/jumpscale/data/serializers/dill.html +++ b/docs/api/jumpscale/data/serializers/dill.html @@ -56,4 +56,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/index.html b/docs/api/jumpscale/data/serializers/index.html index 65b75443c..455a2d4b6 100644 --- a/docs/api/jumpscale/data/serializers/index.html +++ b/docs/api/jumpscale/data/serializers/index.html @@ -25,18 +25,18 @@

    Module jumpscale.data.serializers

    This module does all the work for serialization/deserialization around pickle, base64, json, msgpack, pickle, dill, toml

    -
    JS-NG> obj = {"name":"username", "list":[1,3,4,7], "n":5}
    +
    JS-NG> obj = {"name":"username", "list":[1,3,4,7], "n":5}                                           
     
    -JS-NG> j.data.serializers.json.dumps(obj)
    +JS-NG> j.data.serializers.json.dumps(obj)                                                           
     '{"name": "username", "list": [1, 3, 4, 7], "n": 5}'
     
    -JS-NG> j.data.serializers.toml.dumps(obj)
    +JS-NG> j.data.serializers.toml.dumps(obj)                                                           
     'name = "username"
     list = [1, 3, 4, 7]
     n = 5
     '
     
    -JS-NG> j.data.serializers.yaml.dumps(obj)
    +JS-NG> j.data.serializers.yaml.dumps(obj)                                                           
     'list:
     - 1
     - 3
    @@ -46,7 +46,7 @@ 

    Module jumpscale.data.serializers

    name: username ' -JS-NG> j.data.serializers.msgpack.dumps(obj) +JS-NG> j.data.serializers.msgpack.dumps(obj) b'ƒ¤name¨username¤list”¡n'
    @@ -55,18 +55,18 @@

    Module jumpscale.data.serializers

    """This module does all the work for serialization/deserialization around pickle, base64, json, msgpack, pickle, dill, toml
     ```
    -JS-NG> obj = {"name":"username", "list":[1,3,4,7], "n":5}
    +JS-NG> obj = {"name":"username", "list":[1,3,4,7], "n":5}                                           
     
    -JS-NG> j.data.serializers.json.dumps(obj)
    +JS-NG> j.data.serializers.json.dumps(obj)                                                           
     '{"name": "username", "list": [1, 3, 4, 7], "n": 5}'
     
    -JS-NG> j.data.serializers.toml.dumps(obj)
    +JS-NG> j.data.serializers.toml.dumps(obj)                                                           
     'name = "username"\nlist = [1, 3, 4, 7]\nn = 5\n'
     
    -JS-NG> j.data.serializers.yaml.dumps(obj)
    +JS-NG> j.data.serializers.yaml.dumps(obj)                                                           
     'list:\n- 1\n- 3\n- 4\n- 7\nn: 5\nname: username\n'
     
    -JS-NG> j.data.serializers.msgpack.dumps(obj)
    +JS-NG> j.data.serializers.msgpack.dumps(obj)                                                        
     b'\x83\xa4name\xa8username\xa4list\x94\x01\x03\x04\x07\xa1n\x05'
     ```
     """
    @@ -155,4 +155,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/json.html b/docs/api/jumpscale/data/serializers/json.html index 7500d904b..d468f6f0c 100644 --- a/docs/api/jumpscale/data/serializers/json.html +++ b/docs/api/jumpscale/data/serializers/json.html @@ -225,4 +225,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/lzma.html b/docs/api/jumpscale/data/serializers/lzma.html index 1ab1838d6..7ce8769ee 100644 --- a/docs/api/jumpscale/data/serializers/lzma.html +++ b/docs/api/jumpscale/data/serializers/lzma.html @@ -30,10 +30,10 @@

    Module jumpscale.data.serializers.lzma

    def compress(obj): """compress string with lzma algorithm - + Arguments: obj (string) : the string will be encoded - + Returns: bytes : the compressed bytes """ @@ -41,10 +41,10 @@

    Module jumpscale.data.serializers.lzma

    def decompress(s): """decompress lzma bytes to original obj - + Arguments: s (bytes) : the bytes will be compressed - + Returns: (string) : the decompressed string """ @@ -76,10 +76,10 @@

    Returns

    def compress(obj):
         """compress string with lzma algorithm
    -
    +    
         Arguments:
             obj (string) : the string will be encoded
    -
    +    
         Returns:
             bytes : the compressed bytes
         """
    @@ -101,10 +101,10 @@ 

    Returns

    def decompress(s):
         """decompress lzma bytes to original obj
    -
    +    
         Arguments:
             s (bytes) : the bytes will be compressed
    -
    +    
         Returns:
             (string) : the decompressed string
         """
    @@ -140,4 +140,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/msgpack.html b/docs/api/jumpscale/data/serializers/msgpack.html index bff4401c2..c41e3ce41 100644 --- a/docs/api/jumpscale/data/serializers/msgpack.html +++ b/docs/api/jumpscale/data/serializers/msgpack.html @@ -30,11 +30,11 @@

    Module jumpscale.data.serializers.msgpack

    def dumps(obj): - """dump dict object into msgpack stream - + """dump dict object into msgpack stream + Arguments: - obj (dict) : the dict which will be dumped - + obj (dict) : the dict which will be dumped + Returns: string : the msgpack stream """ @@ -43,10 +43,10 @@

    Module jumpscale.data.serializers.msgpack

    def loads(s): """loads the data from msgpack string into dict - + Arguments: s (string) : the msgpack stream - + Returns: dict : the loaded data from msgpack stram """ @@ -80,11 +80,11 @@

    Returns

    Expand source code
    def dumps(obj):
    -    """dump dict object into msgpack stream
    -
    +    """dump dict object into msgpack stream 
    +    
         Arguments:
    -        obj (dict) : the dict which will be dumped
    -
    +        obj (dict) : the dict which will be dumped     
    +    
         Returns:
             string : the msgpack stream
         """
    @@ -109,10 +109,10 @@ 

    Returns

    def loads(s):
         """loads the data from msgpack string into dict
    -
    +    
         Arguments:
             s (string) : the msgpack stream
    -
    +    
         Returns:
             dict : the loaded data from msgpack stram
         """
    @@ -150,4 +150,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/pickle.html b/docs/api/jumpscale/data/serializers/pickle.html index 67f0663b1..099489912 100644 --- a/docs/api/jumpscale/data/serializers/pickle.html +++ b/docs/api/jumpscale/data/serializers/pickle.html @@ -30,11 +30,11 @@

    Module jumpscale.data.serializers.pickle

    def decompress(obj): - """dump pickle bytes object into string - + """dump pickle bytes object into string + Arguments: - obj (pickle bytes) : the pickle bytes which will be dumped - + obj (pickle bytes) : the pickle bytes which will be dumped + Returns: string : the string """ @@ -43,10 +43,10 @@

    Module jumpscale.data.serializers.pickle

    def compress(obj): """loads the data from pickle string into pickle bytes - + Arguments: obj (string) : the string - + Returns: pickle bytes : the loaded data from pickle stram """ @@ -78,10 +78,10 @@

    Returns

    def compress(obj):
         """loads the data from pickle string into pickle bytes
    -
    +    
         Arguments:
             obj (string) : the string
    -
    +    
         Returns:
             pickle bytes : the loaded data from pickle stram
         """
    @@ -106,11 +106,11 @@ 

    Returns

    Expand source code
    def decompress(obj):
    -    """dump pickle bytes object into string
    -
    +    """dump pickle bytes object into string 
    +    
         Arguments:
    -        obj (pickle bytes) : the pickle bytes which will be dumped
    -
    +        obj (pickle bytes) : the pickle bytes which will be dumped     
    +    
         Returns:
             string : the string
         """
    @@ -146,4 +146,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/toml.html b/docs/api/jumpscale/data/serializers/toml.html index e6fb92892..023524007 100644 --- a/docs/api/jumpscale/data/serializers/toml.html +++ b/docs/api/jumpscale/data/serializers/toml.html @@ -30,11 +30,11 @@

    Module jumpscale.data.serializers.toml

    def dumps(d): - """dump dict object into toml stream - + """dump dict object into toml stream + Arguments: - d (dict) : the dict which will be dumped - + d (dict) : the dict which will be dumped + Returns: string : the toml stream """ @@ -44,10 +44,10 @@

    Module jumpscale.data.serializers.toml

    def loads(s): """loads the data from toml string into dict - + Arguments: s (string) : the toml stream - + Returns: dict : the loaded data from toml stram """ @@ -80,11 +80,11 @@

    Returns

    Expand source code
    def dumps(d):
    -    """dump dict object into toml stream
    -
    +    """dump dict object into toml stream 
    +    
         Arguments:
    -        d (dict) : the dict which will be dumped
    -
    +        d (dict) : the dict which will be dumped     
    +    
         Returns:
             string : the toml stream
         """
    @@ -110,10 +110,10 @@ 

    Returns

    def loads(s):
         """loads the data from toml string into dict
    -
    +    
         Arguments:
             s (string) : the toml stream
    -
    +    
         Returns:
             dict : the loaded data from toml stram
         """
    @@ -150,4 +150,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/serializers/yaml.html b/docs/api/jumpscale/data/serializers/yaml.html index e5056aa0b..0122123e1 100644 --- a/docs/api/jumpscale/data/serializers/yaml.html +++ b/docs/api/jumpscale/data/serializers/yaml.html @@ -30,11 +30,11 @@

    Module jumpscale.data.serializers.yaml

    def dumps(obj): - """dump dict object into yaml stream - + """dump dict object into yaml stream + Arguments: - obj (dict) : the dict which will be dumped - + obj (dict) : the dict which will be dumped + Returns: string : the yaml stream """ @@ -43,10 +43,10 @@

    Module jumpscale.data.serializers.yaml

    def loads(s): """loads the data from yaml string into dict - + Arguments: s (string) : the yaml stream - + Returns: dict : the loaded data from yaml stram """ @@ -78,11 +78,11 @@

    Returns

    Expand source code
    def dumps(obj):
    -    """dump dict object into yaml stream
    -
    +    """dump dict object into yaml stream 
    +    
         Arguments:
    -        obj (dict) : the dict which will be dumped
    -
    +        obj (dict) : the dict which will be dumped     
    +    
         Returns:
             string : the yaml stream
         """
    @@ -107,10 +107,10 @@ 

    Returns

    def loads(s):
         """loads the data from yaml string into dict
    -
    +    
         Arguments:
             s (string) : the yaml stream
    -
    +    
         Returns:
             dict : the loaded data from yaml stram
         """
    @@ -146,4 +146,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/tarfile/index.html b/docs/api/jumpscale/data/tarfile/index.html index dc82c0cba..5124d26bd 100644 --- a/docs/api/jumpscale/data/tarfile/index.html +++ b/docs/api/jumpscale/data/tarfile/index.html @@ -68,4 +68,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/tarfile/tar_file.html b/docs/api/jumpscale/data/tarfile/tar_file.html index 3d715f8e4..a657e3db6 100644 --- a/docs/api/jumpscale/data/tarfile/tar_file.html +++ b/docs/api/jumpscale/data/tarfile/tar_file.html @@ -31,7 +31,7 @@

    Module jumpscale.data.tarfile.tar_file

    def istar(path): """check if the file is .tar format - + Arguments: path (str) : the path for the file """ @@ -40,7 +40,7 @@

    Module jumpscale.data.tarfile.tar_file

    def compress(source, output): """make an archive file from directory or file - + Arguments: source (str) : the path for the file or the directory output (str) : the path for the output @@ -51,7 +51,7 @@

    Module jumpscale.data.tarfile.tar_file

    class Reader: """handle the reading operation on tar file - + Arguments: path (str) : the path for tar file """ @@ -100,7 +100,7 @@

    Arguments

    def compress(source, output):
         """make an archive file from directory or file
    -
    +    
         Arguments:
             source (str) : the path for the file or the directory
             output (str) : the path for the output
    @@ -122,7 +122,7 @@ 

    Arguments

    def istar(path):
         """check if the file is .tar format
    -
    +    
         Arguments:
             path (str) : the path for the file
         """
    @@ -148,7 +148,7 @@ 

    Arguments

    class Reader:
         """handle the reading operation on tar file
    -
    +    
         Arguments:
             path (str) : the path for tar file
         """
    @@ -250,4 +250,4 @@ 

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/terminaltable/index.html b/docs/api/jumpscale/data/terminaltable/index.html index 773c49f7f..f9cdb5bdc 100644 --- a/docs/api/jumpscale/data/terminaltable/index.html +++ b/docs/api/jumpscale/data/terminaltable/index.html @@ -55,7 +55,7 @@

    Module jumpscale.data.terminaltable

    """This module helps around creation of terminal tables
     
    -JS-NG> j.data.terminaltable.print_table("users", [ ["id", "name"], ["1", "ahmed"], ["2", "xmonader"]])
    +JS-NG> j.data.terminaltable.print_table("users", [ ["id", "name"], ["1", "ahmed"], ["2", "xmonader"]])           
     +users----------+
     | id | name     |
     +----+----------+
    @@ -63,8 +63,8 @@ 

    Module jumpscale.data.terminaltable

    | 2 | xmonader | +----+----------+ -JS-NG> tbl = j.data.terminaltable.create("users", [ ["id", "name"], ["1", "ahmed"], ["2", "xmonader"]]) -JS-NG> print(tbl) +JS-NG> tbl = j.data.terminaltable.create("users", [ ["id", "name"], ["1", "ahmed"], ["2", "xmonader"]]) +JS-NG> print(tbl) +users----------+ | id | name | +----+----------+ @@ -161,4 +161,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/text/index.html b/docs/api/jumpscale/data/text/index.html index e278e3f4f..137e465c2 100644 --- a/docs/api/jumpscale/data/text/index.html +++ b/docs/api/jumpscale/data/text/index.html @@ -24,11 +24,11 @@

    Module jumpscale.data.text

    helpers around string manipulation.

    Remove prefix

    -
    JS-NG> j.data.text.removeprefix("ahhmed", "ah")
    +
    JS-NG> j.data.text.removeprefix("ahhmed", "ah")                                                                                                                                          
     'hmed'
     

    Remove suffix

    -
    JS-NG> j.data.text.removesuffix("ahhmed.3bot", ".3bot")
    +
    JS-NG> j.data.text.removesuffix("ahhmed.3bot", ".3bot")                                                                                                                                  
     'ahhmed'
     
    @@ -38,16 +38,16 @@

    Remove suffix

    """helpers around string manipulation.
     
     
    -## Remove prefix
    +## Remove prefix 
     ```
    -JS-NG> j.data.text.removeprefix("ahhmed", "ah")
    +JS-NG> j.data.text.removeprefix("ahhmed", "ah")                                                                                                                                          
     'hmed'
     ```
     
     ## Remove suffix
     
     ```
    -JS-NG> j.data.text.removesuffix("ahhmed.3bot", ".3bot")
    +JS-NG> j.data.text.removesuffix("ahhmed.3bot", ".3bot")                                                                                                                                  
     'ahhmed'
     ```
     """
    @@ -201,4 +201,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/time/index.html b/docs/api/jumpscale/data/time/index.html index d72f02b30..b63c89025 100644 --- a/docs/api/jumpscale/data/time/index.html +++ b/docs/api/jumpscale/data/time/index.html @@ -35,10 +35,10 @@

    TODO: add more explanation here.

    >>> utc <Arrow [2013-05-11T20:23:58.970460+00:00]> ->>> j.data.time.now() +>>> j.data.time.now() <Arrow [2020-04-09T10:19:19.013636+02:00]> ->>> j.data.time.now().shift(hours=15) +>>> j.data.time.now().shift(hours=15) <Arrow [2020-04-10T01:19:23.225311+02:00]> @@ -128,10 +128,10 @@

    TODO: add more explanation here.

    >>> utc <Arrow [2013-05-11T20:23:58.970460+00:00]> ->>> j.data.time.now() +>>> j.data.time.now() <Arrow [2020-04-09T10:19:19.013636+02:00]> ->>> j.data.time.now().shift(hours=15) +>>> j.data.time.now().shift(hours=15) <Arrow [2020-04-10T01:19:23.225311+02:00]> @@ -235,4 +235,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/treemanager/exceptions.html b/docs/api/jumpscale/data/treemanager/exceptions.html new file mode 100644 index 000000000..a181dc642 --- /dev/null +++ b/docs/api/jumpscale/data/treemanager/exceptions.html @@ -0,0 +1,147 @@ + + + + + + +jumpscale.data.treemanager.exceptions API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.data.treemanager.exceptions

    +
    +
    +
    + +Expand source code + +
    from jumpscale.core.exceptions import JSException
    +
    +
    +class NameExistsError(JSException):
    +    pass
    +
    +
    +class EmptyNameError(JSException):
    +    pass
    +
    +
    +class RootRemoveError(JSException):
    +    pass
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class EmptyNameError +(message, category=None, level=None, context=None) +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class EmptyNameError(JSException):
    +    pass
    +
    +

    Ancestors

    +
      +
    • JSException
    • +
    • builtins.Exception
    • +
    • builtins.BaseException
    • +
    +
    +
    +class NameExistsError +(message, category=None, level=None, context=None) +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class NameExistsError(JSException):
    +    pass
    +
    +

    Ancestors

    +
      +
    • JSException
    • +
    • builtins.Exception
    • +
    • builtins.BaseException
    • +
    +
    +
    +class RootRemoveError +(message, category=None, level=None, context=None) +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class RootRemoveError(JSException):
    +    pass
    +
    +

    Ancestors

    +
      +
    • JSException
    • +
    • builtins.Exception
    • +
    • builtins.BaseException
    • +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/data/treemanager/index.html b/docs/api/jumpscale/data/treemanager/index.html new file mode 100644 index 000000000..745744dfc --- /dev/null +++ b/docs/api/jumpscale/data/treemanager/index.html @@ -0,0 +1,80 @@ + + + + + + +jumpscale.data.treemanager API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/data/treemanager/treemanager.html b/docs/api/jumpscale/data/treemanager/treemanager.html new file mode 100644 index 000000000..3402d137d --- /dev/null +++ b/docs/api/jumpscale/data/treemanager/treemanager.html @@ -0,0 +1,1222 @@ + + + + + + +jumpscale.data.treemanager.treemanager API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.data.treemanager.treemanager

    +
    +
    +

    This is a module with a general tree implementation. +A sample usage of the Tree class as a file manager

    +
    if __name__ == "__main__":
    +    tree = Tree()
    +    tree.add_node_by_path("root", {"file_name": "root",
    +                                   "modified": "12/3/2019"})
    +    tree.add_node_by_path("etc", {"file_name": "etc",
    +                                  "modified": "13/3/2018"})
    +    tree.add_node_by_path("etc.hosts", {"file_name": "hosts",
    +                                        "modified": "14/3/2017"})
    +    tree.add_node_by_path("etc.passwd", {"file_name": "passwd",
    +                                         "modified": "14/3/2016"})
    +    pred = lambda x: x.data["modified"].split("/")[-1] < "2018"
    +    too_old = tree.search_custom(pred)
    +    print("Too old files (before 2018):
    +")
    +    for f in too_old:
    +        print(f.name + "
    +")
    +    print("Tree before removing /etc/hosts")
    +    print(tree)
    +    print("Tree after removing /etc/hosts")
    +    tree.remove_node_by_path("etc.hosts")
    +    print(tree)
    +    passwd_file = tree.get_by_path("etc.passwd")
    +    passwd_date = passwd_file.data["modified"]
    +    print("Last time /etc/passwd was modified is: " + passwd_date)
    +
    +
    + +Expand source code + +
    """
    +This is a module with a general tree implementation.
    +A sample usage of the Tree class as a file manager
    +```
    +if __name__ == "__main__":
    +    tree = Tree()
    +    tree.add_node_by_path("root", {"file_name": "root",
    +                                   "modified": "12/3/2019"})
    +    tree.add_node_by_path("etc", {"file_name": "etc",
    +                                  "modified": "13/3/2018"})
    +    tree.add_node_by_path("etc.hosts", {"file_name": "hosts",
    +                                        "modified": "14/3/2017"})
    +    tree.add_node_by_path("etc.passwd", {"file_name": "passwd",
    +                                         "modified": "14/3/2016"})
    +    pred = lambda x: x.data["modified"].split("/")[-1] < "2018"
    +    too_old = tree.search_custom(pred)
    +    print("Too old files (before 2018):\n")
    +    for f in too_old:
    +        print(f.name + "\n")
    +    print("Tree before removing /etc/hosts")
    +    print(tree)
    +    print("Tree after removing /etc/hosts")
    +    tree.remove_node_by_path("etc.hosts")
    +    print(tree)
    +    passwd_file = tree.get_by_path("etc.passwd")
    +    passwd_date = passwd_file.data["modified"]
    +    print("Last time /etc/passwd was modified is: " + passwd_date)
    +```
    +"""
    +from .exceptions import NameExistsError, EmptyNameError, RootRemoveError
    +
    +
    +class TreeNode:
    +    def __init__(self, name, parent, data=None):
    +        """
    +        name     (str)               : The name associated with the node
    +        children (dict[str:TreeNode]): A mapping between names and child nodes
    +        parent   (TreeNode or None)  : The parent TreeNode (None for the root)
    +        data                         : Data associated with the node
    +        """
    +        self.name = name
    +        self.parent = parent
    +        self.data = data
    +        self.children = {}
    +
    +    def add_child(self, node):
    +        """Adds a new child
    +
    +        Args:
    +            node (TreeNode): The node to be added
    +
    +        Returns:
    +            TreeNode: The newly added node
    +        """
    +        child_name = node.name
    +        if child_name in self.children:
    +            raise NameExistsError("A child with the given name already exists")
    +        self.children[child_name] = node
    +        return node
    +
    +    def search_by_name(self, name):
    +        """Search in the node's subtree for nodes with the given name
    +
    +        Args:
    +            name (str): The name to be searched for
    +
    +        Returns:
    +            list of TreeNode: The found nodes
    +        """
    +        return self.search_custom(lambda x: x.name == name)
    +
    +    def search_by_data(self, data):
    +        """Search in the node's subtree for nodes with the given data
    +
    +        Args:
    +            data: The data to be searched for
    +
    +        Returns:
    +            list of TreeNode: The found nodes
    +        """
    +        return self.search_custom(lambda x: x.data == data)
    +
    +    def search_custom(self, func):
    +        """Search the node's subtree the nodes satisfying the given predicate
    +
    +        Args:
    +            func (function): A predicate the recieves a TreeNode
    +
    +        Returns:
    +            list of TreeNode: The nodes found
    +        """
    +        result = []
    +        for v in self.children.values():
    +            result.extend(v.search_custom(func))
    +
    +        if self.name != "" and func(self):
    +            result.append(self)
    +        return result
    +
    +    def get_child_by_name(self, name):
    +        """Get the child with the given name
    +
    +        Args:
    +            name (str): The name of the child
    +
    +        Returns:
    +            TreeNode: The reqiested child. None if it doesn't exist.
    +        """
    +        return self.children.get(name)
    +
    +    def remove_child(self, node):
    +        """Remove the node from the children if it exists
    +
    +        Args:
    +            node (TreeNode): The node to be deleted
    +
    +        Returns:
    +            TreeNode: The deleted node
    +        """
    +        return self.remove_child_by_name(node.name)
    +
    +    def remove_child_by_name(self, name):
    +        """Remove the node from the children
    +
    +        Args:
    +            node (TreeNode): The node to be deleted
    +
    +        Returns:
    +            TreeNode: The deleted node. None if it doesn't exist
    +        """
    +        if name in self.children:
    +            node = self.children[name]
    +            del self.children[name]
    +            return node
    +
    +    def get_path(self):
    +        """Retrieves the path of the node
    +
    +        Returns:
    +            str: The path
    +        """
    +        if self.name == "":
    +            return ""
    +        parent_path = self.parent.get_path()
    +        if parent_path == "":
    +            return self.name
    +        else:
    +            return parent_path + "." + self.name
    +
    +    def __str__(self, indentation=0):
    +        """Returns a string representing the node's subtree
    +
    +        Args:
    +            indentation (int, optional): The level to which the representation\
    +                                         will be indented. Defaults to 0.
    +
    +        Returns:
    +            str: The tree representation
    +        """
    +        result = "\t" * indentation + self._string_repr() + "\n"
    +        for v in self.children.values():
    +            result += v.__str__(indentation + 1)
    +        return result
    +
    +    def _string_repr(self):
    +        """A helper function to return the node's name and data as a string
    +
    +        Returns:
    +            str: The node's string representation
    +        """
    +        if self.name == "":
    +            return "dummy_root"
    +        else:
    +            return self.name + str(self.data).replace("\n", "\\n")
    +
    +
    +class Tree:
    +    """"
    +    A class to represent a tree
    +    """
    +
    +    def __init__(self):
    +        self.root = TreeNode("", None)
    +
    +    def search_by_data(self, data):
    +        """Search the nodes in the tree with the given data
    +
    +        Args:
    +            func (function): A predicate the recieves a TreeNode
    +
    +        Returns:
    +            list of TreeNode: The nodes found
    +        """
    +        return self.root.search_by_data(data)
    +
    +    def search_by_name(self, name):
    +        """Search the nodes in the tree with the passed name
    +
    +        Args:
    +            func (function): A predicate the recieves a TreeNode
    +
    +        Returns:
    +            list of TreeNode: The nodes found
    +        """
    +        return self.root.search_by_name(name)
    +
    +    def search_custom(self, func):
    +        """Search the nodes in the tree satisfying the given predicate
    +
    +        Args:
    +            func (function): A predicate the recieves a TreeNode
    +
    +        Returns:
    +            list of TreeNode: The nodes found
    +        """
    +        return self.root.search_custom(func)
    +
    +    def get_by_path(self, path):
    +        """Retrieves a node designated by the given path
    +
    +        Args:
    +            path (str): A string of names separated by a '.' that reaches\
    +             the desired node when followed
    +
    +            data: The data associated with the newly added node
    +
    +        Returns:
    +            None if an intermidiate node is not found.\
    +            Else the searched node is returned
    +        """
    +        path_arr = path.split(".")
    +        current_node = self.root
    +        for name in path_arr:
    +            next_node = current_node.get_child_by_name(name)
    +            if next_node is None:
    +                return None
    +            current_node = next_node
    +        return current_node
    +
    +    def remove_node(self, node):
    +        """Remove a node from the tree.
    +
    +        Args:
    +            node (TreeNode): The node to be removed
    +        """
    +        if node == self.root:
    +            raise RootRemoveError("Can't remove the root node")
    +        node.parent.remove_child(node)
    +        return node
    +
    +    def add_node_by_path(self, path, data=None):
    +        """Add a node designated by the given path
    +
    +        Args:
    +            path (str): A string of names separated by a '.' that reaches\
    +             the desired node when followed
    +
    +            data: The data associated with the newly added node
    +
    +        Notes:
    +            If intermidiate nodes are not found while traversing the path,\
    +            they are created with data=None.
    +        """
    +        path_arr = path.split(".")
    +        current_node = self.root
    +        for path_name in path_arr[:-1]:
    +            if path_name == "":
    +                raise EmptyNameError("Nodes with empty names are not allowed")
    +            next_node = current_node.get_child_by_name(path_name)
    +            if next_node is None:
    +                next_node = TreeNode(path_name, current_node)
    +                current_node.add_child(next_node)
    +            current_node = next_node
    +        new_node = TreeNode(path_arr[-1], current_node, data)
    +        return current_node.add_child(new_node)
    +
    +    def remove_node_by_path(self, path):
    +        """Remove a node designated by the given path
    +
    +        Args:
    +            path (str): A string of names separated by a '.' that reaches\
    +             the desired node when followed
    +        """
    +        path_arr = path.split(".")
    +        current_node = self.root
    +        parent_node = None
    +        for path_name in path_arr:
    +            next_node = current_node.get_child_by_name(path_name)
    +            if next_node is None:
    +                return None
    +            parent_node = current_node
    +            current_node = next_node
    +        return parent_node.remove_child(current_node)
    +
    +    def __str__(self):
    +        "Return a string representation of the tree"
    +        return self.root.__str__(0)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Tree +
    +
    +

    " +A class to represent a tree

    +
    + +Expand source code + +
    class Tree:
    +    """"
    +    A class to represent a tree
    +    """
    +
    +    def __init__(self):
    +        self.root = TreeNode("", None)
    +
    +    def search_by_data(self, data):
    +        """Search the nodes in the tree with the given data
    +
    +        Args:
    +            func (function): A predicate the recieves a TreeNode
    +
    +        Returns:
    +            list of TreeNode: The nodes found
    +        """
    +        return self.root.search_by_data(data)
    +
    +    def search_by_name(self, name):
    +        """Search the nodes in the tree with the passed name
    +
    +        Args:
    +            func (function): A predicate the recieves a TreeNode
    +
    +        Returns:
    +            list of TreeNode: The nodes found
    +        """
    +        return self.root.search_by_name(name)
    +
    +    def search_custom(self, func):
    +        """Search the nodes in the tree satisfying the given predicate
    +
    +        Args:
    +            func (function): A predicate the recieves a TreeNode
    +
    +        Returns:
    +            list of TreeNode: The nodes found
    +        """
    +        return self.root.search_custom(func)
    +
    +    def get_by_path(self, path):
    +        """Retrieves a node designated by the given path
    +
    +        Args:
    +            path (str): A string of names separated by a '.' that reaches\
    +             the desired node when followed
    +
    +            data: The data associated with the newly added node
    +
    +        Returns:
    +            None if an intermidiate node is not found.\
    +            Else the searched node is returned
    +        """
    +        path_arr = path.split(".")
    +        current_node = self.root
    +        for name in path_arr:
    +            next_node = current_node.get_child_by_name(name)
    +            if next_node is None:
    +                return None
    +            current_node = next_node
    +        return current_node
    +
    +    def remove_node(self, node):
    +        """Remove a node from the tree.
    +
    +        Args:
    +            node (TreeNode): The node to be removed
    +        """
    +        if node == self.root:
    +            raise RootRemoveError("Can't remove the root node")
    +        node.parent.remove_child(node)
    +        return node
    +
    +    def add_node_by_path(self, path, data=None):
    +        """Add a node designated by the given path
    +
    +        Args:
    +            path (str): A string of names separated by a '.' that reaches\
    +             the desired node when followed
    +
    +            data: The data associated with the newly added node
    +
    +        Notes:
    +            If intermidiate nodes are not found while traversing the path,\
    +            they are created with data=None.
    +        """
    +        path_arr = path.split(".")
    +        current_node = self.root
    +        for path_name in path_arr[:-1]:
    +            if path_name == "":
    +                raise EmptyNameError("Nodes with empty names are not allowed")
    +            next_node = current_node.get_child_by_name(path_name)
    +            if next_node is None:
    +                next_node = TreeNode(path_name, current_node)
    +                current_node.add_child(next_node)
    +            current_node = next_node
    +        new_node = TreeNode(path_arr[-1], current_node, data)
    +        return current_node.add_child(new_node)
    +
    +    def remove_node_by_path(self, path):
    +        """Remove a node designated by the given path
    +
    +        Args:
    +            path (str): A string of names separated by a '.' that reaches\
    +             the desired node when followed
    +        """
    +        path_arr = path.split(".")
    +        current_node = self.root
    +        parent_node = None
    +        for path_name in path_arr:
    +            next_node = current_node.get_child_by_name(path_name)
    +            if next_node is None:
    +                return None
    +            parent_node = current_node
    +            current_node = next_node
    +        return parent_node.remove_child(current_node)
    +
    +    def __str__(self):
    +        "Return a string representation of the tree"
    +        return self.root.__str__(0)
    +
    +

    Methods

    +
    +
    +def add_node_by_path(self, path, data=None) +
    +
    +

    Add a node designated by the given path

    +

    Args

    +
    +
    path : str
    +
    A string of names separated by a '.' that reaches +the desired node when followed
    +
    data
    +
    The data associated with the newly added node
    +
    +

    Notes

    +

    If intermidiate nodes are not found while traversing the path, +they are created with data=None.

    +
    + +Expand source code + +
    def add_node_by_path(self, path, data=None):
    +    """Add a node designated by the given path
    +
    +    Args:
    +        path (str): A string of names separated by a '.' that reaches\
    +         the desired node when followed
    +
    +        data: The data associated with the newly added node
    +
    +    Notes:
    +        If intermidiate nodes are not found while traversing the path,\
    +        they are created with data=None.
    +    """
    +    path_arr = path.split(".")
    +    current_node = self.root
    +    for path_name in path_arr[:-1]:
    +        if path_name == "":
    +            raise EmptyNameError("Nodes with empty names are not allowed")
    +        next_node = current_node.get_child_by_name(path_name)
    +        if next_node is None:
    +            next_node = TreeNode(path_name, current_node)
    +            current_node.add_child(next_node)
    +        current_node = next_node
    +    new_node = TreeNode(path_arr[-1], current_node, data)
    +    return current_node.add_child(new_node)
    +
    +
    +
    +def get_by_path(self, path) +
    +
    +

    Retrieves a node designated by the given path

    +

    Args

    +
    +
    path : str
    +
    A string of names separated by a '.' that reaches +the desired node when followed
    +
    data
    +
    The data associated with the newly added node
    +
    +

    Returns

    +

    None if an intermidiate node is not found. +Else the searched node is returned

    +
    + +Expand source code + +
    def get_by_path(self, path):
    +    """Retrieves a node designated by the given path
    +
    +    Args:
    +        path (str): A string of names separated by a '.' that reaches\
    +         the desired node when followed
    +
    +        data: The data associated with the newly added node
    +
    +    Returns:
    +        None if an intermidiate node is not found.\
    +        Else the searched node is returned
    +    """
    +    path_arr = path.split(".")
    +    current_node = self.root
    +    for name in path_arr:
    +        next_node = current_node.get_child_by_name(name)
    +        if next_node is None:
    +            return None
    +        current_node = next_node
    +    return current_node
    +
    +
    +
    +def remove_node(self, node) +
    +
    +

    Remove a node from the tree.

    +

    Args

    +
    +
    node : TreeNode
    +
    The node to be removed
    +
    +
    + +Expand source code + +
    def remove_node(self, node):
    +    """Remove a node from the tree.
    +
    +    Args:
    +        node (TreeNode): The node to be removed
    +    """
    +    if node == self.root:
    +        raise RootRemoveError("Can't remove the root node")
    +    node.parent.remove_child(node)
    +    return node
    +
    +
    +
    +def remove_node_by_path(self, path) +
    +
    +

    Remove a node designated by the given path

    +

    Args

    +
    +
    path : str
    +
    A string of names separated by a '.' that reaches +the desired node when followed
    +
    +
    + +Expand source code + +
    def remove_node_by_path(self, path):
    +    """Remove a node designated by the given path
    +
    +    Args:
    +        path (str): A string of names separated by a '.' that reaches\
    +         the desired node when followed
    +    """
    +    path_arr = path.split(".")
    +    current_node = self.root
    +    parent_node = None
    +    for path_name in path_arr:
    +        next_node = current_node.get_child_by_name(path_name)
    +        if next_node is None:
    +            return None
    +        parent_node = current_node
    +        current_node = next_node
    +    return parent_node.remove_child(current_node)
    +
    +
    +
    +def search_by_data(self, data) +
    +
    +

    Search the nodes in the tree with the given data

    +

    Args

    +
    +
    func : function
    +
    A predicate the recieves a TreeNode
    +
    +

    Returns

    +
    +
    list of TreeNode
    +
    The nodes found
    +
    +
    + +Expand source code + +
    def search_by_data(self, data):
    +    """Search the nodes in the tree with the given data
    +
    +    Args:
    +        func (function): A predicate the recieves a TreeNode
    +
    +    Returns:
    +        list of TreeNode: The nodes found
    +    """
    +    return self.root.search_by_data(data)
    +
    +
    +
    +def search_by_name(self, name) +
    +
    +

    Search the nodes in the tree with the passed name

    +

    Args

    +
    +
    func : function
    +
    A predicate the recieves a TreeNode
    +
    +

    Returns

    +
    +
    list of TreeNode
    +
    The nodes found
    +
    +
    + +Expand source code + +
    def search_by_name(self, name):
    +    """Search the nodes in the tree with the passed name
    +
    +    Args:
    +        func (function): A predicate the recieves a TreeNode
    +
    +    Returns:
    +        list of TreeNode: The nodes found
    +    """
    +    return self.root.search_by_name(name)
    +
    +
    +
    +def search_custom(self, func) +
    +
    +

    Search the nodes in the tree satisfying the given predicate

    +

    Args

    +
    +
    func : function
    +
    A predicate the recieves a TreeNode
    +
    +

    Returns

    +
    +
    list of TreeNode
    +
    The nodes found
    +
    +
    + +Expand source code + +
    def search_custom(self, func):
    +    """Search the nodes in the tree satisfying the given predicate
    +
    +    Args:
    +        func (function): A predicate the recieves a TreeNode
    +
    +    Returns:
    +        list of TreeNode: The nodes found
    +    """
    +    return self.root.search_custom(func)
    +
    +
    +
    +
    +
    +class TreeNode +(name, parent, data=None) +
    +
    +

    name +(str) +: The name associated with the node +children (dict[str:TreeNode]): A mapping between names and child nodes +parent +(TreeNode or None) +: The parent TreeNode (None for the root) +data +: Data associated with the node

    +
    + +Expand source code + +
    class TreeNode:
    +    def __init__(self, name, parent, data=None):
    +        """
    +        name     (str)               : The name associated with the node
    +        children (dict[str:TreeNode]): A mapping between names and child nodes
    +        parent   (TreeNode or None)  : The parent TreeNode (None for the root)
    +        data                         : Data associated with the node
    +        """
    +        self.name = name
    +        self.parent = parent
    +        self.data = data
    +        self.children = {}
    +
    +    def add_child(self, node):
    +        """Adds a new child
    +
    +        Args:
    +            node (TreeNode): The node to be added
    +
    +        Returns:
    +            TreeNode: The newly added node
    +        """
    +        child_name = node.name
    +        if child_name in self.children:
    +            raise NameExistsError("A child with the given name already exists")
    +        self.children[child_name] = node
    +        return node
    +
    +    def search_by_name(self, name):
    +        """Search in the node's subtree for nodes with the given name
    +
    +        Args:
    +            name (str): The name to be searched for
    +
    +        Returns:
    +            list of TreeNode: The found nodes
    +        """
    +        return self.search_custom(lambda x: x.name == name)
    +
    +    def search_by_data(self, data):
    +        """Search in the node's subtree for nodes with the given data
    +
    +        Args:
    +            data: The data to be searched for
    +
    +        Returns:
    +            list of TreeNode: The found nodes
    +        """
    +        return self.search_custom(lambda x: x.data == data)
    +
    +    def search_custom(self, func):
    +        """Search the node's subtree the nodes satisfying the given predicate
    +
    +        Args:
    +            func (function): A predicate the recieves a TreeNode
    +
    +        Returns:
    +            list of TreeNode: The nodes found
    +        """
    +        result = []
    +        for v in self.children.values():
    +            result.extend(v.search_custom(func))
    +
    +        if self.name != "" and func(self):
    +            result.append(self)
    +        return result
    +
    +    def get_child_by_name(self, name):
    +        """Get the child with the given name
    +
    +        Args:
    +            name (str): The name of the child
    +
    +        Returns:
    +            TreeNode: The reqiested child. None if it doesn't exist.
    +        """
    +        return self.children.get(name)
    +
    +    def remove_child(self, node):
    +        """Remove the node from the children if it exists
    +
    +        Args:
    +            node (TreeNode): The node to be deleted
    +
    +        Returns:
    +            TreeNode: The deleted node
    +        """
    +        return self.remove_child_by_name(node.name)
    +
    +    def remove_child_by_name(self, name):
    +        """Remove the node from the children
    +
    +        Args:
    +            node (TreeNode): The node to be deleted
    +
    +        Returns:
    +            TreeNode: The deleted node. None if it doesn't exist
    +        """
    +        if name in self.children:
    +            node = self.children[name]
    +            del self.children[name]
    +            return node
    +
    +    def get_path(self):
    +        """Retrieves the path of the node
    +
    +        Returns:
    +            str: The path
    +        """
    +        if self.name == "":
    +            return ""
    +        parent_path = self.parent.get_path()
    +        if parent_path == "":
    +            return self.name
    +        else:
    +            return parent_path + "." + self.name
    +
    +    def __str__(self, indentation=0):
    +        """Returns a string representing the node's subtree
    +
    +        Args:
    +            indentation (int, optional): The level to which the representation\
    +                                         will be indented. Defaults to 0.
    +
    +        Returns:
    +            str: The tree representation
    +        """
    +        result = "\t" * indentation + self._string_repr() + "\n"
    +        for v in self.children.values():
    +            result += v.__str__(indentation + 1)
    +        return result
    +
    +    def _string_repr(self):
    +        """A helper function to return the node's name and data as a string
    +
    +        Returns:
    +            str: The node's string representation
    +        """
    +        if self.name == "":
    +            return "dummy_root"
    +        else:
    +            return self.name + str(self.data).replace("\n", "\\n")
    +
    +

    Methods

    +
    +
    +def add_child(self, node) +
    +
    +

    Adds a new child

    +

    Args

    +
    +
    node : TreeNode
    +
    The node to be added
    +
    +

    Returns

    +
    +
    TreeNode
    +
    The newly added node
    +
    +
    + +Expand source code + +
    def add_child(self, node):
    +    """Adds a new child
    +
    +    Args:
    +        node (TreeNode): The node to be added
    +
    +    Returns:
    +        TreeNode: The newly added node
    +    """
    +    child_name = node.name
    +    if child_name in self.children:
    +        raise NameExistsError("A child with the given name already exists")
    +    self.children[child_name] = node
    +    return node
    +
    +
    +
    +def get_child_by_name(self, name) +
    +
    +

    Get the child with the given name

    +

    Args

    +
    +
    name : str
    +
    The name of the child
    +
    +

    Returns

    +
    +
    TreeNode
    +
    The reqiested child. None if it doesn't exist.
    +
    +
    + +Expand source code + +
    def get_child_by_name(self, name):
    +    """Get the child with the given name
    +
    +    Args:
    +        name (str): The name of the child
    +
    +    Returns:
    +        TreeNode: The reqiested child. None if it doesn't exist.
    +    """
    +    return self.children.get(name)
    +
    +
    +
    +def get_path(self) +
    +
    +

    Retrieves the path of the node

    +

    Returns

    +
    +
    str
    +
    The path
    +
    +
    + +Expand source code + +
    def get_path(self):
    +    """Retrieves the path of the node
    +
    +    Returns:
    +        str: The path
    +    """
    +    if self.name == "":
    +        return ""
    +    parent_path = self.parent.get_path()
    +    if parent_path == "":
    +        return self.name
    +    else:
    +        return parent_path + "." + self.name
    +
    +
    +
    +def remove_child(self, node) +
    +
    +

    Remove the node from the children if it exists

    +

    Args

    +
    +
    node : TreeNode
    +
    The node to be deleted
    +
    +

    Returns

    +
    +
    TreeNode
    +
    The deleted node
    +
    +
    + +Expand source code + +
    def remove_child(self, node):
    +    """Remove the node from the children if it exists
    +
    +    Args:
    +        node (TreeNode): The node to be deleted
    +
    +    Returns:
    +        TreeNode: The deleted node
    +    """
    +    return self.remove_child_by_name(node.name)
    +
    +
    +
    +def remove_child_by_name(self, name) +
    +
    +

    Remove the node from the children

    +

    Args

    +
    +
    node : TreeNode
    +
    The node to be deleted
    +
    +

    Returns

    +
    +
    TreeNode
    +
    The deleted node. None if it doesn't exist
    +
    +
    + +Expand source code + +
    def remove_child_by_name(self, name):
    +    """Remove the node from the children
    +
    +    Args:
    +        node (TreeNode): The node to be deleted
    +
    +    Returns:
    +        TreeNode: The deleted node. None if it doesn't exist
    +    """
    +    if name in self.children:
    +        node = self.children[name]
    +        del self.children[name]
    +        return node
    +
    +
    +
    +def search_by_data(self, data) +
    +
    +

    Search in the node's subtree for nodes with the given data

    +

    Args

    +
    +
    data
    +
    The data to be searched for
    +
    +

    Returns

    +
    +
    list of TreeNode
    +
    The found nodes
    +
    +
    + +Expand source code + +
    def search_by_data(self, data):
    +    """Search in the node's subtree for nodes with the given data
    +
    +    Args:
    +        data: The data to be searched for
    +
    +    Returns:
    +        list of TreeNode: The found nodes
    +    """
    +    return self.search_custom(lambda x: x.data == data)
    +
    +
    +
    +def search_by_name(self, name) +
    +
    +

    Search in the node's subtree for nodes with the given name

    +

    Args

    +
    +
    name : str
    +
    The name to be searched for
    +
    +

    Returns

    +
    +
    list of TreeNode
    +
    The found nodes
    +
    +
    + +Expand source code + +
    def search_by_name(self, name):
    +    """Search in the node's subtree for nodes with the given name
    +
    +    Args:
    +        name (str): The name to be searched for
    +
    +    Returns:
    +        list of TreeNode: The found nodes
    +    """
    +    return self.search_custom(lambda x: x.name == name)
    +
    +
    +
    +def search_custom(self, func) +
    +
    +

    Search the node's subtree the nodes satisfying the given predicate

    +

    Args

    +
    +
    func : function
    +
    A predicate the recieves a TreeNode
    +
    +

    Returns

    +
    +
    list of TreeNode
    +
    The nodes found
    +
    +
    + +Expand source code + +
    def search_custom(self, func):
    +    """Search the node's subtree the nodes satisfying the given predicate
    +
    +    Args:
    +        func (function): A predicate the recieves a TreeNode
    +
    +    Returns:
    +        list of TreeNode: The nodes found
    +    """
    +    result = []
    +    for v in self.children.values():
    +        result.extend(v.search_custom(func))
    +
    +    if self.name != "" and func(self):
    +        result.append(self)
    +    return result
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/data/types/index.html b/docs/api/jumpscale/data/types/index.html index 5b640776d..64664b232 100644 --- a/docs/api/jumpscale/data/types/index.html +++ b/docs/api/jumpscale/data/types/index.html @@ -73,4 +73,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/types/pritypes.html b/docs/api/jumpscale/data/types/pritypes.html index 716122332..d72646cdb 100644 --- a/docs/api/jumpscale/data/types/pritypes.html +++ b/docs/api/jumpscale/data/types/pritypes.html @@ -52,7 +52,7 @@

    Module jumpscale.data.types.pritypes

    def check(self, value): """Check whether provided string represent integer value - + Arguments: value (str) """ @@ -64,7 +64,7 @@

    Module jumpscale.data.types.pritypes

    def from_str(self, value): """get integer value from tha string - + Arguments: value (str) """ @@ -111,7 +111,7 @@

    Module jumpscale.data.types.pritypes

    def check(self, value): """Check whether provided string represent integer value - + Arguments: value (str) """ @@ -123,7 +123,7 @@

    Module jumpscale.data.types.pritypes

    def from_str(self, value): """get integer value from tha string - + Arguments: value (str) """ @@ -140,7 +140,7 @@

    Module jumpscale.data.types.pritypes

    def check(self, value): """Check whether provided string represent JSObject value. (Any string will do). - + Arguments: value (str) """ @@ -148,7 +148,7 @@

    Module jumpscale.data.types.pritypes

    def from_str(self, value): """Return value as is. - + Arguments: value (str) """ @@ -163,10 +163,10 @@

    Module jumpscale.data.types.pritypes

    def _deep_check(self, value): """Check that the value represents a list with proper elements of the specified subtype. - + Args: value (list): The list to be checked. - + Returns: Boolean: True if the list is valid. """ @@ -181,10 +181,10 @@

    Module jumpscale.data.types.pritypes

    def check(self, value): """Check that the value represents a list with proper elements of the specified subtype. - + Args: value (list): The list to be checked. - + Returns: Boolean: True if the list is valid. """ @@ -199,10 +199,10 @@

    Module jumpscale.data.types.pritypes

    def _deep_parse(self, value): """parses the subelements (if they are of different python type it's converted using the subtype parser) - + Args: value (list): The list to be parsed. - + Returns: list: The parsed list. """ @@ -217,10 +217,10 @@

    Module jumpscale.data.types.pritypes

    def from_str(self, value): """parses the string value into a list. - + Args: value (str): The string to be parsed. - + Returns: list: The parsed list. """ @@ -240,11 +240,11 @@

    Module jumpscale.data.types.pritypes

    4. "F" -> Float 5. "L.*" -> List with subtype .* 6. "" -> empty defaults to String - + Args: type_str (str): type description. default_value (any, optional): The default value. Defaults to None. - + Returns: Object: A js type object. """ @@ -303,11 +303,11 @@

    Returns

    4. "F" -> Float 5. "L.*" -> List with subtype .* 6. "" -> empty defaults to String - + Args: type_str (str): type description. default_value (any, optional): The default value. Defaults to None. - + Returns: Object: A js type object. """ @@ -430,7 +430,7 @@

    Arguments

    def check(self, value): """Check whether provided string represent integer value - + Arguments: value (str) """ @@ -442,7 +442,7 @@

    Arguments

    def from_str(self, value): """get integer value from tha string - + Arguments: value (str) """ @@ -466,7 +466,7 @@

    Arguments

    def check(self, value):
         """Check whether provided string represent integer value
    -
    +    
         Arguments:
             value (str)
         """
    @@ -490,7 +490,7 @@ 

    Arguments

    def from_str(self, value):
         """get integer value from tha string
    -
    +    
         Arguments:
             value (str)
         """
    @@ -520,7 +520,7 @@ 

    Arguments

    def check(self, value): """Check whether provided string represent integer value - + Arguments: value (str) """ @@ -532,7 +532,7 @@

    Arguments

    def from_str(self, value): """get integer value from tha string - + Arguments: value (str) """ @@ -556,7 +556,7 @@

    Arguments

    def check(self, value):
         """Check whether provided string represent integer value
    -
    +    
         Arguments:
             value (str)
         """
    @@ -580,7 +580,7 @@ 

    Arguments

    def from_str(self, value):
         """get integer value from tha string
    -
    +    
         Arguments:
             value (str)
         """
    @@ -609,7 +609,7 @@ 

    Arguments

    def check(self, value): """Check whether provided string represent JSObject value. (Any string will do). - + Arguments: value (str) """ @@ -617,7 +617,7 @@

    Arguments

    def from_str(self, value): """Return value as is. - + Arguments: value (str) """ @@ -638,7 +638,7 @@

    Arguments

    def check(self, value):
         """Check whether provided string represent JSObject value. (Any string will do).
    -
    +    
         Arguments:
             value (str)
         """
    @@ -658,7 +658,7 @@ 

    Arguments

    def from_str(self, value):
         """Return value as is.
    -
    +    
         Arguments:
             value (str)
         """
    @@ -685,10 +685,10 @@ 

    Arguments

    def _deep_check(self, value): """Check that the value represents a list with proper elements of the specified subtype. - + Args: value (list): The list to be checked. - + Returns: Boolean: True if the list is valid. """ @@ -703,10 +703,10 @@

    Arguments

    def check(self, value): """Check that the value represents a list with proper elements of the specified subtype. - + Args: value (list): The list to be checked. - + Returns: Boolean: True if the list is valid. """ @@ -721,10 +721,10 @@

    Arguments

    def _deep_parse(self, value): """parses the subelements (if they are of different python type it's converted using the subtype parser) - + Args: value (list): The list to be parsed. - + Returns: list: The parsed list. """ @@ -739,10 +739,10 @@

    Arguments

    def from_str(self, value): """parses the string value into a list. - + Args: value (str): The string to be parsed. - + Returns: list: The parsed list. """ @@ -774,10 +774,10 @@

    Returns

    def check(self, value):
         """Check that the value represents a list with proper elements of the specified subtype.
    -
    +    
         Args:
             value (list): The list to be checked.
    -
    +    
         Returns:
             Boolean: True if the list is valid.
         """
    @@ -812,10 +812,10 @@ 

    Returns

    def from_str(self, value):
         """parses the string value into a list.
    -
    +    
         Args:
             value (str): The string to be parsed.
    -
    +    
         Returns:
             list: The parsed list.
         """
    @@ -955,4 +955,4 @@ 

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/data/types/types.html b/docs/api/jumpscale/data/types/types.html index 50cc410cc..5f18f2c8e 100644 --- a/docs/api/jumpscale/data/types/types.html +++ b/docs/api/jumpscale/data/types/types.html @@ -2032,4 +2032,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/entry_points/index.html b/docs/api/jumpscale/entry_points/index.html index ab9747db7..baf3b707e 100644 --- a/docs/api/jumpscale/entry_points/index.html +++ b/docs/api/jumpscale/entry_points/index.html @@ -41,6 +41,10 @@

    Sub-modules

    ``` ~> poetry run jsync …

    +
    jumpscale.entry_points.threebot
    +
    +
    +

    @@ -66,6 +70,7 @@

    Index

  • jumpscale.entry_points.jsctl
  • jumpscale.entry_points.jsng
  • jumpscale.entry_points.jsync
  • +
  • jumpscale.entry_points.threebot
  • @@ -75,4 +80,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/entry_points/jsctl.html b/docs/api/jumpscale/entry_points/jsctl.html index 75286403a..d95bf4410 100644 --- a/docs/api/jumpscale/entry_points/jsctl.html +++ b/docs/api/jumpscale/entry_points/jsctl.html @@ -293,4 +293,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/entry_points/jsng.html b/docs/api/jumpscale/entry_points/jsng.html index 62e1cf752..c061ed75f 100644 --- a/docs/api/jumpscale/entry_points/jsng.html +++ b/docs/api/jumpscale/entry_points/jsng.html @@ -91,4 +91,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/entry_points/jsync.html b/docs/api/jumpscale/entry_points/jsync.html index 4a004024e..12399ca2b 100644 --- a/docs/api/jumpscale/entry_points/jsync.html +++ b/docs/api/jumpscale/entry_points/jsync.html @@ -126,4 +126,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/entry_points/threebot.html b/docs/api/jumpscale/entry_points/threebot.html new file mode 100644 index 000000000..8bfdc807f --- /dev/null +++ b/docs/api/jumpscale/entry_points/threebot.html @@ -0,0 +1,465 @@ + + + + + + +jumpscale.entry_points.threebot API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.entry_points.threebot

    +
    +
    +
    + +Expand source code + +
    from gevent import monkey
    +
    +monkey.patch_all(subprocess=False)  # noqa: E402
    +
    +import sys
    +import click
    +import tempfile
    +import subprocess
    +
    +from jumpscale.loader import j
    +from jumpscale.sals.nginx.nginx import PORTS
    +from jumpscale.packages.admin.actors.wallet import Wallet
    +
    +SERVICES_PORTS = {"nginx": 8999, "nginx_http": 80, "nginx_https": 443, "gedis": 16000}
    +
    +THREEBOT_DEPS_BINS = ["nginx", "redis-server", "tmux", "git"]
    +
    +
    +def check_for_bins():
    +    for b in THREEBOT_DEPS_BINS:
    +        notfoundbins = []
    +        if not j.sals.process.is_installed(b):
    +            notfoundbins.append(b)
    +
    +    if notfoundbins:
    +        bins = ",".join(notfoundbins)
    +        j.logger.error(
    +            f"{bins} not found in $PATH. Please check https://github.com/threefoldtech/js-sdk/blob/development/docs/wiki/installation.md for more info on installation requirements"
    +        )
    +        exit(1)
    +    else:
    +        bins = ",".join(THREEBOT_DEPS_BINS)
    +        j.logger.info(f"✅ binaries {bins} required are installed.")
    +
    +
    +def test_privileged_ports_bind():
    +    config = b"""\
    +worker_processes  1;
    +daemon off;
    +error_log stderr notice;
    +pid /tmp/nginxtest.pid;
    +events { worker_connections  1024; }
    +http {
    +    access_log off;
    +    server {
    +        listen       80;
    +        server_name  localhost;
    +    }
    +}
    +    """
    +    with tempfile.NamedTemporaryFile("w+b") as file:
    +        file.write(config)
    +        file.flush()
    +        proc = subprocess.Popen(["nginx", "-t", "-c", file.name], stderr=subprocess.PIPE)
    +        proc.wait()
    +        for line in proc.stderr.readlines():
    +            j.logger.debug(line.decode().strip())
    +        return proc.returncode == 0
    +
    +
    +@click.command()
    +@click.option("--identity", default=None, help="threebot name(i,e name.3bot)")
    +@click.option("--domain", default=None, help="threebot domain")
    +@click.option("--email", default=None, help="threebot ssl email")
    +@click.option("--development", default=False, is_flag=True, help="start in development mode (no identity is required)")
    +@click.option("--background/--no-background", default=False, help="threebot name(i,e name.3bot)")
    +@click.option(
    +    "--local/--no-local", default=False, help="run threebot server on none privileged ports instead of 80/443"
    +)
    +@click.option("--cert/--no-cert", default=True, help="Generate certificates for ssl virtual hosts")
    +def start(identity=None, background=False, local=False, cert=True, development=False, domain=None, email=None):
    +    """start 3Bot server after making sure identity is ok
    +    It will start with the default identity in j.me, if you'd like to specify an identity
    +    please pass the optional arguments
    +
    +    usage: threebot start
    +    or: threebot start --identity <identity>
    +
    +    Args:
    +        identity (str, optional): threebot name. Defaults to None.
    +    """
    +    if j.config.get("ANNOUNCED") is None:
    +        j.config.set("ANNOUNCED", False)
    +
    +    create_wallets_if_not_exists()
    +    check_for_bins()
    +    PORTS.init_default_ports(local)
    +    SERVICES_PORTS["nginx_http"] = PORTS.HTTP
    +    SERVICES_PORTS["nginx_https"] = PORTS.HTTPS
    +
    +    if not development and identity:
    +        if j.core.identity.find(identity):
    +            print(f"Setting default identity to {identity}\nStarting threebot server...")
    +            j.core.identity.set_default(identity)
    +            j.core.identity.me.save()
    +        else:
    +            j.tools.console.printcolors(
    +                "{RED}Identity %s is not set, please configure it or start with the default one{{RESET}}" % identity
    +            )
    +            sys.exit(1)
    +
    +    if not development and not j.core.identity.list_all():
    +        identity_info = j.core.identity.ask()
    +        me = j.core.identity.new(
    +            "default",
    +            tname=identity_info.identity,
    +            email=identity_info.email,
    +            words=identity_info.words,
    +        )
    +        me.register()
    +        me.save()
    +
    +    used_ports = []
    +    ports_error_msg = ""
    +    for service_name, service_port in SERVICES_PORTS.items():
    +        if j.sals.process.is_port_listening(service_port):
    +            used_ports.append((service_name, service_port))
    +            ports_error_msg += f" {service_name}:{service_port}"
    +
    +    msg = (
    +        f"{{RED}}Threebot server is running already or ports{{CYAN}}{ports_error_msg}{{RED}}\n"
    +        "are being held by your system\n"
    +        f"Please use {{WHITE}}threebot stop{{RED}} to stop your threebot server or free the ports to be able to start the threebot server{{RESET}}"
    +    )
    +
    +    canbind = test_privileged_ports_bind()
    +    if not local and not canbind:
    +        j.tools.console.printcolors(
    +            "{RED}Nginx could not bind on privileged ports{RESET}, please run with local flag or give nginx extra permission 'sudo setcap CAP_NET_BIND_SERVICE=+eip $(which nginx)'"
    +        )
    +        sys.exit(1)
    +    if used_ports:
    +        j.tools.console.printcolors(msg)
    +        sys.exit(1)
    +
    +    if background:
    +        cmd = j.tools.startupcmd.get("threebot_default")
    +        cmd.start_cmd = f"jsng 'j.servers.threebot.start_default(wait=True, local={local}, domain={domain}, email={email}, cert={cert})'"
    +        cmd.process_strings_regex = [".*threebot_default.sh"]
    +        cmd.ports = [8000, 8999]
    +        cmd.start()
    +        for service_name, service_port in SERVICES_PORTS.items():
    +            if not j.sals.nettools.wait_connection_test("127.0.0.1", service_port, timeout=15):
    +                j.tools.console.printcolors(
    +                    f"{{RED}}Could not start threebot server. Service: {service_name}, Port {service_port} couldn't start in 15 seconds. Please try again {{RESET}}"
    +                )
    +                sys.exit(1)
    +
    +        print("\n✅ Threebot server started\n")
    +        if j.sals.process.in_host():
    +            j.tools.console.printcolors(
    +                f"{{WHITE}}Visit admin dashboard at: {{GREEN}}http://localhost:{PORTS.HTTP}/\n{{RESET}}"
    +            )
    +    else:
    +        j.servers.threebot.start_default(wait=True, local=local, domain=domain, email=email, cert=cert)
    +
    +
    +@click.command()
    +def stop():
    +    """stops threebot server"""
    +    threebot_pids = j.sals.process.get_pids("threebot")
    +    if len(threebot_pids) > 1:  # Check if threebot was started in foreground
    +        # Kill all other processes other than `threebot stop`
    +        mypid = j.sals.process.get_my_process().pid  # `threebot stop` pid
    +        for pid in threebot_pids:
    +            if pid != mypid:
    +                j.sals.process.kill(pid)
    +    else:
    +        threebot_cmd = j.tools.startupcmd.get("threebot_default")
    +        threebot_cmd.stop()
    +        j.servers.threebot.get().redis.stop()
    +        j.tools.nginx.get("default").stop()
    +    print("Threebot server Stopped")
    +
    +
    +@click.command()
    +def status():
    +    """return the status of threebot server"""
    +    if j.servers.threebot.get().is_running():
    +        j.tools.console.printcolors("Server is {GREEN}running{RESET}")
    +    else:
    +        j.tools.console.printcolors("Server is {RED}stopped{RESET}")
    +
    +
    +@click.command()
    +@click.option("--identity", default=None, help="threebot name(i,e name.3bot)")
    +@click.option("--domain", default=None, help="threebot domain")
    +@click.option("--email", default=None, help="threebot ssl email")
    +@click.option("--development", default=False, is_flag=True, help="start in development mode (no identity is required)")
    +@click.option("--background/--no-background", default=False, help="threebot name(i,e name.3bot)")
    +@click.option(
    +    "--local/--no-local", default=False, help="run threebot server on none privileged ports instead of 80/443"
    +)
    +@click.pass_context
    +def restart(ctx, identity=None, background=False, local=False, development=False, domain=None, email=None):
    +    """restart threebot server"""
    +    ctx.invoke(stop)
    +    ctx.invoke(
    +        start,
    +        identity=identity,
    +        background=background,
    +        local=local,
    +        development=development,
    +        domain=domain,
    +        email=email,
    +    )
    +
    +
    +@click.command()
    +@click.option("--all", default=False, is_flag=True, help="delete all of jumpscale config")
    +def clean(all=False):
    +    """deletes previous configurations of jumpscale"""
    +    config_root = j.core.config.config_root
    +
    +    if all:
    +        try:
    +            print("cleaning alerts...")
    +            try:
    +                j.tools.alerthandler.reset()
    +            except Exception as e:
    +                print("failed to clean up alerts")
    +                print(f"exception was {e} for debugging")
    +
    +            answer = j.tools.console.ask_yes_no(f"Do you want to remove {config_root} ? ")
    +            if answer == "y":
    +                j.sals.fs.rmtree(config_root)
    +                print("Previous configuration is deleted.")
    +
    +        except Exception as e:
    +            print(f"couldn't remove {config_root}")
    +            print(f"exception for debugging {e}")
    +
    +
    +@click.command()
    +@click.option("-o", "--output", default="export.tar.gz", help="exported output file")
    +def export(output):
    +    j.tools.export.export_threebot_state(output)
    +
    +
    +@click.group()
    +def cli():
    +    pass
    +
    +
    +def have_wallets():
    +    wallets = j.clients.stellar.list_all()
    +    for wallet_name in wallets:
    +        wallet = j.clients.stellar.get(wallet_name)
    +        if wallet.network.value == "STD":
    +            return True
    +    return False
    +
    +
    +def create_main_wallet(wallet_name):
    +    wallet_actor = Wallet()
    +    try:
    +        wallet_actor.create_wallet(wallet_name)
    +    except Exception as e:
    +        j.logger.error(str(e))
    +
    +
    +def create_wallets_if_not_exists():
    +    main_wallet = have_wallets()
    +    if not j.core.identity.is_configured:
    +        j.logger.warning("skipping wallets creation, identity isn't configured yet")
    +        return
    +    else:
    +        if not main_wallet:
    +            create_main_wallet("main")
    +
    +
    +cli.add_command(start)
    +cli.add_command(stop)
    +cli.add_command(status)
    +cli.add_command(restart)
    +cli.add_command(clean)
    +cli.add_command(export)
    +
    +
    +if __name__ == "__main__":
    +    cli()
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def check_for_bins() +
    +
    +
    +
    + +Expand source code + +
    def check_for_bins():
    +    for b in THREEBOT_DEPS_BINS:
    +        notfoundbins = []
    +        if not j.sals.process.is_installed(b):
    +            notfoundbins.append(b)
    +
    +    if notfoundbins:
    +        bins = ",".join(notfoundbins)
    +        j.logger.error(
    +            f"{bins} not found in $PATH. Please check https://github.com/threefoldtech/js-sdk/blob/development/docs/wiki/installation.md for more info on installation requirements"
    +        )
    +        exit(1)
    +    else:
    +        bins = ",".join(THREEBOT_DEPS_BINS)
    +        j.logger.info(f"✅ binaries {bins} required are installed.")
    +
    +
    +
    +def create_main_wallet(wallet_name) +
    +
    +
    +
    + +Expand source code + +
    def create_main_wallet(wallet_name):
    +    wallet_actor = Wallet()
    +    try:
    +        wallet_actor.create_wallet(wallet_name)
    +    except Exception as e:
    +        j.logger.error(str(e))
    +
    +
    +
    +def create_wallets_if_not_exists() +
    +
    +
    +
    + +Expand source code + +
    def create_wallets_if_not_exists():
    +    main_wallet = have_wallets()
    +    if not j.core.identity.is_configured:
    +        j.logger.warning("skipping wallets creation, identity isn't configured yet")
    +        return
    +    else:
    +        if not main_wallet:
    +            create_main_wallet("main")
    +
    +
    +
    +def have_wallets() +
    +
    +
    +
    + +Expand source code + +
    def have_wallets():
    +    wallets = j.clients.stellar.list_all()
    +    for wallet_name in wallets:
    +        wallet = j.clients.stellar.get(wallet_name)
    +        if wallet.network.value == "STD":
    +            return True
    +    return False
    +
    +
    +
    +def test_privileged_ports_bind() +
    +
    +
    +
    + +Expand source code + +
    def test_privileged_ports_bind():
    +    config = b"""\
    +worker_processes  1;
    +daemon off;
    +error_log stderr notice;
    +pid /tmp/nginxtest.pid;
    +events { worker_connections  1024; }
    +http {
    +    access_log off;
    +    server {
    +        listen       80;
    +        server_name  localhost;
    +    }
    +}
    +    """
    +    with tempfile.NamedTemporaryFile("w+b") as file:
    +        file.write(config)
    +        file.flush()
    +        proc = subprocess.Popen(["nginx", "-t", "-c", file.name], stderr=subprocess.PIPE)
    +        proc.wait()
    +        for line in proc.stderr.readlines():
    +            j.logger.debug(line.decode().strip())
    +        return proc.returncode == 0
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/index.html b/docs/api/jumpscale/index.html index c6238ffdd..f45f38f9f 100644 --- a/docs/api/jumpscale/index.html +++ b/docs/api/jumpscale/index.html @@ -42,10 +42,18 @@

    Sub-modules

    +
    jumpscale.install
    +
    +
    +
    jumpscale.loader

    This module exposes a j object, which contains a reference to all sub namespaces and modules available under jumpscale …

    +
    jumpscale.packages
    +
    +
    +
    jumpscale.sals
    @@ -83,7 +91,9 @@

    Index

  • jumpscale.core
  • jumpscale.data
  • jumpscale.entry_points
  • +
  • jumpscale.install
  • jumpscale.loader
  • +
  • jumpscale.packages
  • jumpscale.sals
  • jumpscale.servers
  • jumpscale.shell
  • @@ -97,4 +107,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/install/certbot/certbot_cronjob.html b/docs/api/jumpscale/install/certbot/certbot_cronjob.html new file mode 100644 index 000000000..d277c15e0 --- /dev/null +++ b/docs/api/jumpscale/install/certbot/certbot_cronjob.html @@ -0,0 +1,240 @@ + + + + + + +jumpscale.install.certbot.certbot_cronjob API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.install.certbot.certbot_cronjob

    +
    +
    +

    Certbot Custom Cronjob +This script used as custom certbot cronjob +- Make sure that the certificates for all domains are managed be certbot. +- Renew certificates.

    +

    Cronjob Flow: +1. If domain has a no/pre-fetched certificate "Not managed by certbot" +–> Obtain and install a new certificate +2. If domain has a managed certificate "Managed by certbot" +–> continue for other domains +3. Renew all managed certificate if needed (Renewal will only occur if expiration is within 30 days)

    +
    + +Expand source code + +
    """Certbot Custom Cronjob
    +This script used as custom certbot cronjob
    +- Make sure that the certificates for all domains are managed be certbot.
    +- Renew certificates.
    +
    +Cronjob Flow:
    +1. If domain has a no/pre-fetched certificate "Not managed by certbot"  --> Obtain and install a new certificate
    +2. If domain has a managed certificate "Managed by certbot"             --> continue for other domains
    +3. Renew all managed certificate if needed (Renewal will only occur if expiration is within 30 days)
    +"""
    +
    +from jumpscale.loader import j
    +
    +
    +def check_managed_certificate(certbot):
    +    """Check if the certificate managed by certbot or not
    +
    +    Args:
    +        certbot (Certbot): certbot object that contains website configurations
    +
    +    Returns:
    +        bool: True if managed by certbot, False otherwise
    +    """
    +    cmd = certbot.run_cmd
    +    cmd.insert(1, "certificates")
    +
    +    rc, out, err = j.sals.process.execute(cmd)
    +
    +    if rc > 0:
    +        j.logger.error(f"Check certificate failed {out}\n{err}")
    +        return False
    +    elif out.count("No certificates found") > 0:
    +        j.logger.info(f"No certificate managed by certbot for {certbot.domain}")
    +        return False
    +
    +    j.logger.info(f"Certificate managed by certbot for {certbot.domain}")
    +    return True
    +
    +
    +def main():
    +    j.logger.info("Start Certbot Cronjob")
    +    threebot_server = j.servers.threebot.get("default")
    +    renew_command = []
    +    for p in threebot_server.packages.list_all():
    +        package = threebot_server.packages.get(p)
    +        package.nginx_config.nginx.cert = False  # To disable generate a certificate
    +        package.nginx_config.apply(write_config=False)
    +        j.logger.debug(f"Check Package:{p}")
    +        for w in package.nginx_config.nginx.websites.list_all():
    +            website = package.nginx_config.nginx.websites.get(w)
    +            if website.domain:
    +                certbot = website.certbot
    +                if check_managed_certificate(certbot):
    +                    # Certificate managed by certbot, Execute renew after check all certificates
    +                    if not renew_command:  # We need to run it one time to get renew command
    +                        renew_command = certbot.renew_cmd
    +                    continue
    +                else:
    +                    # Certifcate not managed by certbot, Run certbot to get a new one
    +                    j.logger.info("New certificate will created to be managed by certbot")
    +                    website.obtain_and_install_certifcate()
    +
    +    j.logger.info("Excute certbot renew to renew all the managed certificates")
    +    j.logger.info(f"{' '.join(renew_command)}")
    +    rc, out, err = j.sals.process.execute(renew_command)
    +
    +    if rc > 0:
    +        j.logger.error(f"Renew certificates failed {out}\n{err}")
    +    else:
    +        j.logger.info(f"Certificates Renewed\n{out}")
    +
    +
    +if __name__ == "__main__":
    +    main()
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def check_managed_certificate(certbot) +
    +
    +

    Check if the certificate managed by certbot or not

    +

    Args

    +
    +
    certbot : Certbot
    +
    certbot object that contains website configurations
    +
    +

    Returns

    +
    +
    bool
    +
    True if managed by certbot, False otherwise
    +
    +
    + +Expand source code + +
    def check_managed_certificate(certbot):
    +    """Check if the certificate managed by certbot or not
    +
    +    Args:
    +        certbot (Certbot): certbot object that contains website configurations
    +
    +    Returns:
    +        bool: True if managed by certbot, False otherwise
    +    """
    +    cmd = certbot.run_cmd
    +    cmd.insert(1, "certificates")
    +
    +    rc, out, err = j.sals.process.execute(cmd)
    +
    +    if rc > 0:
    +        j.logger.error(f"Check certificate failed {out}\n{err}")
    +        return False
    +    elif out.count("No certificates found") > 0:
    +        j.logger.info(f"No certificate managed by certbot for {certbot.domain}")
    +        return False
    +
    +    j.logger.info(f"Certificate managed by certbot for {certbot.domain}")
    +    return True
    +
    +
    +
    +def main() +
    +
    +
    +
    + +Expand source code + +
    def main():
    +    j.logger.info("Start Certbot Cronjob")
    +    threebot_server = j.servers.threebot.get("default")
    +    renew_command = []
    +    for p in threebot_server.packages.list_all():
    +        package = threebot_server.packages.get(p)
    +        package.nginx_config.nginx.cert = False  # To disable generate a certificate
    +        package.nginx_config.apply(write_config=False)
    +        j.logger.debug(f"Check Package:{p}")
    +        for w in package.nginx_config.nginx.websites.list_all():
    +            website = package.nginx_config.nginx.websites.get(w)
    +            if website.domain:
    +                certbot = website.certbot
    +                if check_managed_certificate(certbot):
    +                    # Certificate managed by certbot, Execute renew after check all certificates
    +                    if not renew_command:  # We need to run it one time to get renew command
    +                        renew_command = certbot.renew_cmd
    +                    continue
    +                else:
    +                    # Certifcate not managed by certbot, Run certbot to get a new one
    +                    j.logger.info("New certificate will created to be managed by certbot")
    +                    website.obtain_and_install_certifcate()
    +
    +    j.logger.info("Excute certbot renew to renew all the managed certificates")
    +    j.logger.info(f"{' '.join(renew_command)}")
    +    rc, out, err = j.sals.process.execute(renew_command)
    +
    +    if rc > 0:
    +        j.logger.error(f"Renew certificates failed {out}\n{err}")
    +    else:
    +        j.logger.info(f"Certificates Renewed\n{out}")
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/install/certbot/index.html b/docs/api/jumpscale/install/certbot/index.html new file mode 100644 index 000000000..c7488fd3b --- /dev/null +++ b/docs/api/jumpscale/install/certbot/index.html @@ -0,0 +1,68 @@ + + + + + + +jumpscale.install.certbot API documentation + + + + + + + + + + + +
    +
    +
    +

    Namespace jumpscale.install.certbot

    +
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.install.certbot.certbot_cronjob
    +
    +

    Certbot Custom Cronjob +This script used as custom certbot cronjob +- Make sure that the certificates for all domains are managed be certbot. +- Renew …

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/install/codeserver_install.html b/docs/api/jumpscale/install/codeserver_install.html new file mode 100644 index 000000000..fc5f5fb00 --- /dev/null +++ b/docs/api/jumpscale/install/codeserver_install.html @@ -0,0 +1,66 @@ + + + + + + +jumpscale.install.codeserver_install API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.install.codeserver_install

    +
    +
    +
    + +Expand source code + +
    from jumpscale.core.base import StoredFactory
    +from jumpscale.servers.threebot.threebot import ThreebotServer
    +from jumpscale.loader import j
    +
    +factory = StoredFactory(ThreebotServer)
    +threebot = factory.get("default")
    +threebot.packages.add(j.sals.fs.parent(j.packages.codeserver.__file__))
    +threebot.save()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/install/index.html b/docs/api/jumpscale/install/index.html new file mode 100644 index 000000000..f720ad50e --- /dev/null +++ b/docs/api/jumpscale/install/index.html @@ -0,0 +1,70 @@ + + + + + + +jumpscale.install API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/loader.html b/docs/api/jumpscale/loader.html index 3c7cbaed6..d6a2f322d 100644 --- a/docs/api/jumpscale/loader.html +++ b/docs/api/jumpscale/loader.html @@ -481,7 +481,7 @@

    Instance variables

    return self.core.logging
    -
    var sals
    +
    var packages
    @@ -512,7 +512,7 @@

    Instance variables

    return new_module
    -
    var servers
    +
    var sals
    @@ -543,7 +543,7 @@

    Instance variables

    return new_module
    -
    var shell
    +
    var servers
    @@ -574,7 +574,7 @@

    Instance variables

    return new_module
    -
    var threesdk
    +
    var shell
    @@ -673,10 +673,10 @@

    J<
  • exceptions
  • install
  • logger
  • +
  • packages
  • sals
  • servers
  • shell
  • -
  • threesdk
  • tools
  • @@ -689,4 +689,4 @@

    J<

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/actors/admin.html b/docs/api/jumpscale/packages/admin/actors/admin.html new file mode 100644 index 000000000..8cb011605 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/actors/admin.html @@ -0,0 +1,2196 @@ + + + + + + +jumpscale.packages.admin.actors.admin API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.actors.admin

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +from requests import HTTPError
    +import json
    +
    +
    +class Admin(BaseActor):
    +    @actor_method
    +    def list_admins(self) -> str:
    +        admins = list(set(j.core.identity.me.admins))
    +        return j.data.serializers.json.dumps({"data": admins})
    +
    +    @actor_method
    +    def add_admin(self, name: str):
    +        if name in j.core.identity.me.admins:
    +            raise j.exceptions.Value(f"Admin {name} already exists")
    +        j.core.identity.me.admins.append(name)
    +        j.core.identity.me.save()
    +
    +    @actor_method
    +    def delete_admin(self, name: str):
    +        if name not in j.core.identity.me.admins:
    +            raise j.exceptions.Value(f"Admin {name} does not exist")
    +        j.core.identity.me.admins.remove(name)
    +        j.core.identity.me.save()
    +
    +    @actor_method
    +    def list_identities(self) -> str:
    +        identities = j.core.identity.list_all()
    +        identity_data = {}
    +        for identity_name in identities:
    +            try:
    +                identity = j.core.identity.get(identity_name)
    +                if identity.tid < 0:
    +                    continue
    +                identity_dict = identity.to_dict()
    +                identity_dict["instance_name"] = identity.instance_name
    +                identity_dict.pop("__words")
    +            except Exception as e:
    +                j.logger.exception("error", exception=e)
    +                # TODO: include traceback
    +                j.tools.alerthandler.alert_raise(
    +                    app_name="admin",
    +                    category="internal_errors",
    +                    message=f"failed to get identity {identity_name} info due to error {str(e)}",
    +                    alert_type="exception",
    +                )
    +                continue
    +            identity_data[identity_name] = identity_dict
    +        return j.data.serializers.json.dumps({"data": identity_data})
    +
    +    @actor_method
    +    def get_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            identity = j.core.identity.get(identity_instance_name)
    +
    +            return j.data.serializers.json.dumps(
    +                {
    +                    "data": {
    +                        "instance_name": identity.instance_name,
    +                        "name": identity.tname,
    +                        "email": identity.email,
    +                        "tid": identity.tid,
    +                        "words": identity.words,
    +                        "admins": identity.admins,
    +                    }
    +                }
    +            )
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    @actor_method
    +    def get_current_identity_name(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.core.identity.me.instance_name})
    +
    +    @actor_method
    +    def set_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            j.core.identity.set_default(identity_instance_name)
    +
    +            return j.data.serializers.json.dumps({"data": {"instance_name": identity_instance_name}})
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    def generate_mnemonic(self) -> str:
    +        words = j.data.encryption.generate_mnemonic()
    +        return j.data.serializers.json.dumps({"data": words})
    +
    +    @actor_method
    +    def check_tname_exists(self, tname) -> str:
    +        try:
    +            j.core.identity.get_user(tname)
    +            return j.data.serializers.json.dumps({"data": True})
    +        except j.exceptions.NotFound:
    +            return j.data.serializers.json.dumps({"data": False})
    +
    +    @actor_method
    +    def check_identity_instance_name(self, name) -> str:
    +        if name in j.core.identity.list_all():
    +            return j.data.serializers.json.dumps({"data": True})
    +        return j.data.serializers.json.dumps({"data": False})
    +
    +    @actor_method
    +    def add_identity(
    +        self,
    +        display_name: str,
    +        tname: str,
    +        email: str,
    +        words: str,
    +        admins: list,
    +    ) -> str:
    +        checked_admins = []
    +        for admin in admins:
    +            if type(admin) is str and not admin.endswith(".3bot"):
    +                admin = admin + ".3bot"
    +            checked_admins.append(admin)
    +
    +        if not display_name.isidentifier() or not display_name.islower():
    +            raise j.exceptions.Value(
    +                "The display name must be a lowercase valid python identitifier (English letters, underscores, and numbers not starting with a number)."
    +            )
    +        identity_instance_name = display_name
    +        if identity_instance_name in j.core.identity.list_all():
    +            raise j.exceptions.Value("Identity with the same name already exists")
    +
    +        try:
    +            new_identity = j.core.identity.new(
    +                name=identity_instance_name,
    +                tname=tname,
    +                email=email,
    +                words=words,
    +                admins=checked_admins,
    +            )
    +            new_identity.register()
    +            new_identity.save()
    +        except HTTPError as e:
    +            j.core.identity.delete(identity_instance_name)
    +            try:
    +                raise j.exceptions.Value(j.data.serializers.json.loads(e.response.content)["error"])
    +            except (KeyError, json.decoder.JSONDecodeError):
    +                # it failed for some reason other than Bad Request error
    +                raise j.exceptions.Value(str(e))
    +        except Exception as e:
    +            # register sometimes throws exceptions other than HTTP like Input
    +            j.core.identity.delete(identity_instance_name)
    +            raise j.exceptions.Value(str(e))
    +        return j.data.serializers.json.dumps({"data": "New identity successfully created and registered"})
    +
    +    @actor_method
    +    def get_config(self) -> str:
    +        config_obj = j.core.config.get_config()
    +        return j.data.serializers.json.dumps({"data": config_obj})
    +
    +    @actor_method
    +    def get_sdk_version(self) -> str:
    +        import importlib_metadata as metadata
    +
    +        packages = ["js-ng", "js-sdk"]
    +        data = {}
    +        for package in packages:
    +            data[package] = metadata.version(package)
    +        return j.data.serializers.json.dumps({"data": data})
    +
    +    @actor_method
    +    def delete_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            identity = j.core.identity.get(identity_instance_name)
    +            if identity.instance_name == j.core.identity.me.instance_name:
    +                return j.data.serializers.json.dumps({"data": "Cannot delete current default identity"})
    +
    +            j.core.identity.delete(identity_instance_name)
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} deleted successfully"})
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    @actor_method
    +    def list_sshkeys(self) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def add_sshkey(self, key_id, sshkey) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        if key_id not in sshkeys:
    +            sshkeys[key_id] = sshkey
    +            j.core.config.set("SSH_KEYS", sshkeys)
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def delete_sshkey(self, key_id) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        if key_id in sshkeys:
    +            sshkeys.pop(key_id)
    +            j.core.config.set("SSH_KEYS", sshkeys)
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def get_developer_options(self) -> str:
    +        test_cert = j.core.config.set_default("TEST_CERT", False)
    +        over_provision = j.core.config.set_default("OVER_PROVISIONING", False)
    +        escalation_emails = j.core.config.set_default("ESCALATION_EMAILS_ENABLED", False)
    +        sort_nodes_by_sru = j.core.config.set_default("SORT_NODES_BY_SRU", False)
    +        return j.data.serializers.json.dumps(
    +            {
    +                "data": {
    +                    "test_cert": test_cert,
    +                    "over_provision": over_provision,
    +                    "escalation_emails": escalation_emails,
    +                    "sort_nodes_by_sru": sort_nodes_by_sru,
    +                }
    +            }
    +        )
    +
    +    @actor_method
    +    def set_developer_options(
    +        self,
    +        test_cert: bool,
    +        over_provision: bool,
    +        sort_nodes_by_sru: bool,
    +        escalation_emails: bool,
    +    ) -> str:
    +        j.core.config.set("TEST_CERT", test_cert)
    +        j.core.config.set("OVER_PROVISIONING", over_provision)
    +        j.core.config.set("ESCALATION_EMAILS_ENABLED", escalation_emails)
    +        j.core.config.set("SORT_NODES_BY_SRU", sort_nodes_by_sru)
    +        return j.data.serializers.json.dumps(
    +            {
    +                "data": {
    +                    "test_cert": test_cert,
    +                    "over_provision": over_provision,
    +                    "escalation_emails": escalation_emails,
    +                    "sort_nodes_by_sru": sort_nodes_by_sru,
    +                }
    +            }
    +        )
    +
    +    @actor_method
    +    def clear_blocked_nodes(self) -> str:
    +        j.sals.reservation_chatflow.reservation_chatflow.clear_blocked_nodes()
    +        return j.data.serializers.json.dumps({"data": "blocked nodes got cleared successfully."})
    +
    +    @actor_method
    +    def clear_blocked_managed_domains(self) -> str:
    +        j.sals.marketplace.deployer.clear_blocked_managed_domains()
    +        return j.data.serializers.json.dumps({"data": "blocked managed domains got cleared successfully."})
    +
    +    @actor_method
    +    def get_email_server_config(self) -> str:
    +        email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +        email_server_config.setdefault("host", "")
    +        email_server_config.setdefault("port", "")
    +        email_server_config.setdefault("username", "")
    +        email_server_config.setdefault("password", "")
    +        return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +    @actor_method
    +    def set_email_server_config(self, host="", port="", username="", password="") -> str:
    +        email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +        email_server_config = {"host": host, "port": port, "username": username, "password": password}
    +        j.core.config.set("EMAIL_SERVER_CONFIG", email_server_config)
    +        return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +    @actor_method
    +    def list_escalation_emails(self) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    @actor_method
    +    def add_escalation_email(self, email) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        if email not in escalation_emails:
    +            escalation_emails.append(email)
    +            j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    @actor_method
    +    def delete_escalation_email(self, email) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        if email in escalation_emails:
    +            escalation_emails.remove(email)
    +            j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    def get_notifications(self) -> str:
    +        notifications = []
    +        if j.tools.notificationsqueue.count() >= 10:
    +            notifications = j.tools.notificationsqueue.fetch()
    +        else:
    +            notifications = j.tools.notificationsqueue.fetch(10)
    +        ret = [notification.json for notification in notifications]
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def get_notifications_count(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.tools.notificationsqueue.count()})
    +
    +
    +Actor = Admin
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Admin +
    +
    +
    +
    + +Expand source code + +
    class Admin(BaseActor):
    +    @actor_method
    +    def list_admins(self) -> str:
    +        admins = list(set(j.core.identity.me.admins))
    +        return j.data.serializers.json.dumps({"data": admins})
    +
    +    @actor_method
    +    def add_admin(self, name: str):
    +        if name in j.core.identity.me.admins:
    +            raise j.exceptions.Value(f"Admin {name} already exists")
    +        j.core.identity.me.admins.append(name)
    +        j.core.identity.me.save()
    +
    +    @actor_method
    +    def delete_admin(self, name: str):
    +        if name not in j.core.identity.me.admins:
    +            raise j.exceptions.Value(f"Admin {name} does not exist")
    +        j.core.identity.me.admins.remove(name)
    +        j.core.identity.me.save()
    +
    +    @actor_method
    +    def list_identities(self) -> str:
    +        identities = j.core.identity.list_all()
    +        identity_data = {}
    +        for identity_name in identities:
    +            try:
    +                identity = j.core.identity.get(identity_name)
    +                if identity.tid < 0:
    +                    continue
    +                identity_dict = identity.to_dict()
    +                identity_dict["instance_name"] = identity.instance_name
    +                identity_dict.pop("__words")
    +            except Exception as e:
    +                j.logger.exception("error", exception=e)
    +                # TODO: include traceback
    +                j.tools.alerthandler.alert_raise(
    +                    app_name="admin",
    +                    category="internal_errors",
    +                    message=f"failed to get identity {identity_name} info due to error {str(e)}",
    +                    alert_type="exception",
    +                )
    +                continue
    +            identity_data[identity_name] = identity_dict
    +        return j.data.serializers.json.dumps({"data": identity_data})
    +
    +    @actor_method
    +    def get_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            identity = j.core.identity.get(identity_instance_name)
    +
    +            return j.data.serializers.json.dumps(
    +                {
    +                    "data": {
    +                        "instance_name": identity.instance_name,
    +                        "name": identity.tname,
    +                        "email": identity.email,
    +                        "tid": identity.tid,
    +                        "words": identity.words,
    +                        "admins": identity.admins,
    +                    }
    +                }
    +            )
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    @actor_method
    +    def get_current_identity_name(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.core.identity.me.instance_name})
    +
    +    @actor_method
    +    def set_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            j.core.identity.set_default(identity_instance_name)
    +
    +            return j.data.serializers.json.dumps({"data": {"instance_name": identity_instance_name}})
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    def generate_mnemonic(self) -> str:
    +        words = j.data.encryption.generate_mnemonic()
    +        return j.data.serializers.json.dumps({"data": words})
    +
    +    @actor_method
    +    def check_tname_exists(self, tname) -> str:
    +        try:
    +            j.core.identity.get_user(tname)
    +            return j.data.serializers.json.dumps({"data": True})
    +        except j.exceptions.NotFound:
    +            return j.data.serializers.json.dumps({"data": False})
    +
    +    @actor_method
    +    def check_identity_instance_name(self, name) -> str:
    +        if name in j.core.identity.list_all():
    +            return j.data.serializers.json.dumps({"data": True})
    +        return j.data.serializers.json.dumps({"data": False})
    +
    +    @actor_method
    +    def add_identity(
    +        self,
    +        display_name: str,
    +        tname: str,
    +        email: str,
    +        words: str,
    +        admins: list,
    +    ) -> str:
    +        checked_admins = []
    +        for admin in admins:
    +            if type(admin) is str and not admin.endswith(".3bot"):
    +                admin = admin + ".3bot"
    +            checked_admins.append(admin)
    +
    +        if not display_name.isidentifier() or not display_name.islower():
    +            raise j.exceptions.Value(
    +                "The display name must be a lowercase valid python identitifier (English letters, underscores, and numbers not starting with a number)."
    +            )
    +        identity_instance_name = display_name
    +        if identity_instance_name in j.core.identity.list_all():
    +            raise j.exceptions.Value("Identity with the same name already exists")
    +
    +        try:
    +            new_identity = j.core.identity.new(
    +                name=identity_instance_name,
    +                tname=tname,
    +                email=email,
    +                words=words,
    +                admins=checked_admins,
    +            )
    +            new_identity.register()
    +            new_identity.save()
    +        except HTTPError as e:
    +            j.core.identity.delete(identity_instance_name)
    +            try:
    +                raise j.exceptions.Value(j.data.serializers.json.loads(e.response.content)["error"])
    +            except (KeyError, json.decoder.JSONDecodeError):
    +                # it failed for some reason other than Bad Request error
    +                raise j.exceptions.Value(str(e))
    +        except Exception as e:
    +            # register sometimes throws exceptions other than HTTP like Input
    +            j.core.identity.delete(identity_instance_name)
    +            raise j.exceptions.Value(str(e))
    +        return j.data.serializers.json.dumps({"data": "New identity successfully created and registered"})
    +
    +    @actor_method
    +    def get_config(self) -> str:
    +        config_obj = j.core.config.get_config()
    +        return j.data.serializers.json.dumps({"data": config_obj})
    +
    +    @actor_method
    +    def get_sdk_version(self) -> str:
    +        import importlib_metadata as metadata
    +
    +        packages = ["js-ng", "js-sdk"]
    +        data = {}
    +        for package in packages:
    +            data[package] = metadata.version(package)
    +        return j.data.serializers.json.dumps({"data": data})
    +
    +    @actor_method
    +    def delete_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            identity = j.core.identity.get(identity_instance_name)
    +            if identity.instance_name == j.core.identity.me.instance_name:
    +                return j.data.serializers.json.dumps({"data": "Cannot delete current default identity"})
    +
    +            j.core.identity.delete(identity_instance_name)
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} deleted successfully"})
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    @actor_method
    +    def list_sshkeys(self) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def add_sshkey(self, key_id, sshkey) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        if key_id not in sshkeys:
    +            sshkeys[key_id] = sshkey
    +            j.core.config.set("SSH_KEYS", sshkeys)
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def delete_sshkey(self, key_id) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        if key_id in sshkeys:
    +            sshkeys.pop(key_id)
    +            j.core.config.set("SSH_KEYS", sshkeys)
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def get_developer_options(self) -> str:
    +        test_cert = j.core.config.set_default("TEST_CERT", False)
    +        over_provision = j.core.config.set_default("OVER_PROVISIONING", False)
    +        escalation_emails = j.core.config.set_default("ESCALATION_EMAILS_ENABLED", False)
    +        sort_nodes_by_sru = j.core.config.set_default("SORT_NODES_BY_SRU", False)
    +        return j.data.serializers.json.dumps(
    +            {
    +                "data": {
    +                    "test_cert": test_cert,
    +                    "over_provision": over_provision,
    +                    "escalation_emails": escalation_emails,
    +                    "sort_nodes_by_sru": sort_nodes_by_sru,
    +                }
    +            }
    +        )
    +
    +    @actor_method
    +    def set_developer_options(
    +        self,
    +        test_cert: bool,
    +        over_provision: bool,
    +        sort_nodes_by_sru: bool,
    +        escalation_emails: bool,
    +    ) -> str:
    +        j.core.config.set("TEST_CERT", test_cert)
    +        j.core.config.set("OVER_PROVISIONING", over_provision)
    +        j.core.config.set("ESCALATION_EMAILS_ENABLED", escalation_emails)
    +        j.core.config.set("SORT_NODES_BY_SRU", sort_nodes_by_sru)
    +        return j.data.serializers.json.dumps(
    +            {
    +                "data": {
    +                    "test_cert": test_cert,
    +                    "over_provision": over_provision,
    +                    "escalation_emails": escalation_emails,
    +                    "sort_nodes_by_sru": sort_nodes_by_sru,
    +                }
    +            }
    +        )
    +
    +    @actor_method
    +    def clear_blocked_nodes(self) -> str:
    +        j.sals.reservation_chatflow.reservation_chatflow.clear_blocked_nodes()
    +        return j.data.serializers.json.dumps({"data": "blocked nodes got cleared successfully."})
    +
    +    @actor_method
    +    def clear_blocked_managed_domains(self) -> str:
    +        j.sals.marketplace.deployer.clear_blocked_managed_domains()
    +        return j.data.serializers.json.dumps({"data": "blocked managed domains got cleared successfully."})
    +
    +    @actor_method
    +    def get_email_server_config(self) -> str:
    +        email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +        email_server_config.setdefault("host", "")
    +        email_server_config.setdefault("port", "")
    +        email_server_config.setdefault("username", "")
    +        email_server_config.setdefault("password", "")
    +        return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +    @actor_method
    +    def set_email_server_config(self, host="", port="", username="", password="") -> str:
    +        email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +        email_server_config = {"host": host, "port": port, "username": username, "password": password}
    +        j.core.config.set("EMAIL_SERVER_CONFIG", email_server_config)
    +        return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +    @actor_method
    +    def list_escalation_emails(self) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    @actor_method
    +    def add_escalation_email(self, email) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        if email not in escalation_emails:
    +            escalation_emails.append(email)
    +            j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    @actor_method
    +    def delete_escalation_email(self, email) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        if email in escalation_emails:
    +            escalation_emails.remove(email)
    +            j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    def get_notifications(self) -> str:
    +        notifications = []
    +        if j.tools.notificationsqueue.count() >= 10:
    +            notifications = j.tools.notificationsqueue.fetch()
    +        else:
    +            notifications = j.tools.notificationsqueue.fetch(10)
    +        ret = [notification.json for notification in notifications]
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def get_notifications_count(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.tools.notificationsqueue.count()})
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def add_admin(self, name: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_admin(self, name: str):
    +    if name in j.core.identity.me.admins:
    +        raise j.exceptions.Value(f"Admin {name} already exists")
    +    j.core.identity.me.admins.append(name)
    +    j.core.identity.me.save()
    +
    +
    +
    +def add_escalation_email(self, email) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_escalation_email(self, email) -> str:
    +    escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +    if email not in escalation_emails:
    +        escalation_emails.append(email)
    +        j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +    return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +
    +
    +def add_identity(self, display_name: str, tname: str, email: str, words: str, admins: list) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_identity(
    +    self,
    +    display_name: str,
    +    tname: str,
    +    email: str,
    +    words: str,
    +    admins: list,
    +) -> str:
    +    checked_admins = []
    +    for admin in admins:
    +        if type(admin) is str and not admin.endswith(".3bot"):
    +            admin = admin + ".3bot"
    +        checked_admins.append(admin)
    +
    +    if not display_name.isidentifier() or not display_name.islower():
    +        raise j.exceptions.Value(
    +            "The display name must be a lowercase valid python identitifier (English letters, underscores, and numbers not starting with a number)."
    +        )
    +    identity_instance_name = display_name
    +    if identity_instance_name in j.core.identity.list_all():
    +        raise j.exceptions.Value("Identity with the same name already exists")
    +
    +    try:
    +        new_identity = j.core.identity.new(
    +            name=identity_instance_name,
    +            tname=tname,
    +            email=email,
    +            words=words,
    +            admins=checked_admins,
    +        )
    +        new_identity.register()
    +        new_identity.save()
    +    except HTTPError as e:
    +        j.core.identity.delete(identity_instance_name)
    +        try:
    +            raise j.exceptions.Value(j.data.serializers.json.loads(e.response.content)["error"])
    +        except (KeyError, json.decoder.JSONDecodeError):
    +            # it failed for some reason other than Bad Request error
    +            raise j.exceptions.Value(str(e))
    +    except Exception as e:
    +        # register sometimes throws exceptions other than HTTP like Input
    +        j.core.identity.delete(identity_instance_name)
    +        raise j.exceptions.Value(str(e))
    +    return j.data.serializers.json.dumps({"data": "New identity successfully created and registered"})
    +
    +
    +
    +def add_sshkey(self, key_id, sshkey) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_sshkey(self, key_id, sshkey) -> str:
    +    sshkeys = j.core.config.get("SSH_KEYS", {})
    +    if key_id not in sshkeys:
    +        sshkeys[key_id] = sshkey
    +        j.core.config.set("SSH_KEYS", sshkeys)
    +    return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +
    +
    +def check_identity_instance_name(self, name) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def check_identity_instance_name(self, name) -> str:
    +    if name in j.core.identity.list_all():
    +        return j.data.serializers.json.dumps({"data": True})
    +    return j.data.serializers.json.dumps({"data": False})
    +
    +
    +
    +def check_tname_exists(self, tname) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def check_tname_exists(self, tname) -> str:
    +    try:
    +        j.core.identity.get_user(tname)
    +        return j.data.serializers.json.dumps({"data": True})
    +    except j.exceptions.NotFound:
    +        return j.data.serializers.json.dumps({"data": False})
    +
    +
    +
    +def clear_blocked_managed_domains(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def clear_blocked_managed_domains(self) -> str:
    +    j.sals.marketplace.deployer.clear_blocked_managed_domains()
    +    return j.data.serializers.json.dumps({"data": "blocked managed domains got cleared successfully."})
    +
    +
    +
    +def clear_blocked_nodes(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def clear_blocked_nodes(self) -> str:
    +    j.sals.reservation_chatflow.reservation_chatflow.clear_blocked_nodes()
    +    return j.data.serializers.json.dumps({"data": "blocked nodes got cleared successfully."})
    +
    +
    +
    +def delete_admin(self, name: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_admin(self, name: str):
    +    if name not in j.core.identity.me.admins:
    +        raise j.exceptions.Value(f"Admin {name} does not exist")
    +    j.core.identity.me.admins.remove(name)
    +    j.core.identity.me.save()
    +
    +
    +
    +def delete_escalation_email(self, email) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_escalation_email(self, email) -> str:
    +    escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +    if email in escalation_emails:
    +        escalation_emails.remove(email)
    +        j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +    return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +
    +
    +def delete_identity(self, identity_instance_name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_identity(self, identity_instance_name: str) -> str:
    +    identity_names = j.core.identity.list_all()
    +    if identity_instance_name in identity_names:
    +        identity = j.core.identity.get(identity_instance_name)
    +        if identity.instance_name == j.core.identity.me.instance_name:
    +            return j.data.serializers.json.dumps({"data": "Cannot delete current default identity"})
    +
    +        j.core.identity.delete(identity_instance_name)
    +        return j.data.serializers.json.dumps({"data": f"{identity_instance_name} deleted successfully"})
    +    else:
    +        return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +
    +
    +def delete_sshkey(self, key_id) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_sshkey(self, key_id) -> str:
    +    sshkeys = j.core.config.get("SSH_KEYS", {})
    +    if key_id in sshkeys:
    +        sshkeys.pop(key_id)
    +        j.core.config.set("SSH_KEYS", sshkeys)
    +    return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +
    +
    +def generate_mnemonic(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    def generate_mnemonic(self) -> str:
    +    words = j.data.encryption.generate_mnemonic()
    +    return j.data.serializers.json.dumps({"data": words})
    +
    +
    +
    +def get_config(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_config(self) -> str:
    +    config_obj = j.core.config.get_config()
    +    return j.data.serializers.json.dumps({"data": config_obj})
    +
    +
    +
    +def get_current_identity_name(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_current_identity_name(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.core.identity.me.instance_name})
    +
    +
    +
    +def get_developer_options(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_developer_options(self) -> str:
    +    test_cert = j.core.config.set_default("TEST_CERT", False)
    +    over_provision = j.core.config.set_default("OVER_PROVISIONING", False)
    +    escalation_emails = j.core.config.set_default("ESCALATION_EMAILS_ENABLED", False)
    +    sort_nodes_by_sru = j.core.config.set_default("SORT_NODES_BY_SRU", False)
    +    return j.data.serializers.json.dumps(
    +        {
    +            "data": {
    +                "test_cert": test_cert,
    +                "over_provision": over_provision,
    +                "escalation_emails": escalation_emails,
    +                "sort_nodes_by_sru": sort_nodes_by_sru,
    +            }
    +        }
    +    )
    +
    +
    +
    +def get_email_server_config(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_email_server_config(self) -> str:
    +    email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +    email_server_config.setdefault("host", "")
    +    email_server_config.setdefault("port", "")
    +    email_server_config.setdefault("username", "")
    +    email_server_config.setdefault("password", "")
    +    return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +
    +
    +def get_identity(self, identity_instance_name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_identity(self, identity_instance_name: str) -> str:
    +    identity_names = j.core.identity.list_all()
    +    if identity_instance_name in identity_names:
    +        identity = j.core.identity.get(identity_instance_name)
    +
    +        return j.data.serializers.json.dumps(
    +            {
    +                "data": {
    +                    "instance_name": identity.instance_name,
    +                    "name": identity.tname,
    +                    "email": identity.email,
    +                    "tid": identity.tid,
    +                    "words": identity.words,
    +                    "admins": identity.admins,
    +                }
    +            }
    +        )
    +    else:
    +        return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +
    +
    +def get_notifications(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    def get_notifications(self) -> str:
    +    notifications = []
    +    if j.tools.notificationsqueue.count() >= 10:
    +        notifications = j.tools.notificationsqueue.fetch()
    +    else:
    +        notifications = j.tools.notificationsqueue.fetch(10)
    +    ret = [notification.json for notification in notifications]
    +    return j.data.serializers.json.dumps({"data": ret})
    +
    +
    +
    +def get_notifications_count(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_notifications_count(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.tools.notificationsqueue.count()})
    +
    +
    +
    +def get_sdk_version(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_sdk_version(self) -> str:
    +    import importlib_metadata as metadata
    +
    +    packages = ["js-ng", "js-sdk"]
    +    data = {}
    +    for package in packages:
    +        data[package] = metadata.version(package)
    +    return j.data.serializers.json.dumps({"data": data})
    +
    +
    +
    +def list_admins(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_admins(self) -> str:
    +    admins = list(set(j.core.identity.me.admins))
    +    return j.data.serializers.json.dumps({"data": admins})
    +
    +
    +
    +def list_escalation_emails(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_escalation_emails(self) -> str:
    +    escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +    return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +
    +
    +def list_identities(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_identities(self) -> str:
    +    identities = j.core.identity.list_all()
    +    identity_data = {}
    +    for identity_name in identities:
    +        try:
    +            identity = j.core.identity.get(identity_name)
    +            if identity.tid < 0:
    +                continue
    +            identity_dict = identity.to_dict()
    +            identity_dict["instance_name"] = identity.instance_name
    +            identity_dict.pop("__words")
    +        except Exception as e:
    +            j.logger.exception("error", exception=e)
    +            # TODO: include traceback
    +            j.tools.alerthandler.alert_raise(
    +                app_name="admin",
    +                category="internal_errors",
    +                message=f"failed to get identity {identity_name} info due to error {str(e)}",
    +                alert_type="exception",
    +            )
    +            continue
    +        identity_data[identity_name] = identity_dict
    +    return j.data.serializers.json.dumps({"data": identity_data})
    +
    +
    +
    +def list_sshkeys(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_sshkeys(self) -> str:
    +    sshkeys = j.core.config.get("SSH_KEYS", {})
    +    return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +
    +
    +def set_developer_options(self, test_cert: bool, over_provision: bool, sort_nodes_by_sru: bool, escalation_emails: bool) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def set_developer_options(
    +    self,
    +    test_cert: bool,
    +    over_provision: bool,
    +    sort_nodes_by_sru: bool,
    +    escalation_emails: bool,
    +) -> str:
    +    j.core.config.set("TEST_CERT", test_cert)
    +    j.core.config.set("OVER_PROVISIONING", over_provision)
    +    j.core.config.set("ESCALATION_EMAILS_ENABLED", escalation_emails)
    +    j.core.config.set("SORT_NODES_BY_SRU", sort_nodes_by_sru)
    +    return j.data.serializers.json.dumps(
    +        {
    +            "data": {
    +                "test_cert": test_cert,
    +                "over_provision": over_provision,
    +                "escalation_emails": escalation_emails,
    +                "sort_nodes_by_sru": sort_nodes_by_sru,
    +            }
    +        }
    +    )
    +
    +
    +
    +def set_email_server_config(self, host='', port='', username='', password='') ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def set_email_server_config(self, host="", port="", username="", password="") -> str:
    +    email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +    email_server_config = {"host": host, "port": port, "username": username, "password": password}
    +    j.core.config.set("EMAIL_SERVER_CONFIG", email_server_config)
    +    return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +
    +
    +def set_identity(self, identity_instance_name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def set_identity(self, identity_instance_name: str) -> str:
    +    identity_names = j.core.identity.list_all()
    +    if identity_instance_name in identity_names:
    +        j.core.identity.set_default(identity_instance_name)
    +
    +        return j.data.serializers.json.dumps({"data": {"instance_name": identity_instance_name}})
    +    else:
    +        return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class Admin(BaseActor):
    +    @actor_method
    +    def list_admins(self) -> str:
    +        admins = list(set(j.core.identity.me.admins))
    +        return j.data.serializers.json.dumps({"data": admins})
    +
    +    @actor_method
    +    def add_admin(self, name: str):
    +        if name in j.core.identity.me.admins:
    +            raise j.exceptions.Value(f"Admin {name} already exists")
    +        j.core.identity.me.admins.append(name)
    +        j.core.identity.me.save()
    +
    +    @actor_method
    +    def delete_admin(self, name: str):
    +        if name not in j.core.identity.me.admins:
    +            raise j.exceptions.Value(f"Admin {name} does not exist")
    +        j.core.identity.me.admins.remove(name)
    +        j.core.identity.me.save()
    +
    +    @actor_method
    +    def list_identities(self) -> str:
    +        identities = j.core.identity.list_all()
    +        identity_data = {}
    +        for identity_name in identities:
    +            try:
    +                identity = j.core.identity.get(identity_name)
    +                if identity.tid < 0:
    +                    continue
    +                identity_dict = identity.to_dict()
    +                identity_dict["instance_name"] = identity.instance_name
    +                identity_dict.pop("__words")
    +            except Exception as e:
    +                j.logger.exception("error", exception=e)
    +                # TODO: include traceback
    +                j.tools.alerthandler.alert_raise(
    +                    app_name="admin",
    +                    category="internal_errors",
    +                    message=f"failed to get identity {identity_name} info due to error {str(e)}",
    +                    alert_type="exception",
    +                )
    +                continue
    +            identity_data[identity_name] = identity_dict
    +        return j.data.serializers.json.dumps({"data": identity_data})
    +
    +    @actor_method
    +    def get_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            identity = j.core.identity.get(identity_instance_name)
    +
    +            return j.data.serializers.json.dumps(
    +                {
    +                    "data": {
    +                        "instance_name": identity.instance_name,
    +                        "name": identity.tname,
    +                        "email": identity.email,
    +                        "tid": identity.tid,
    +                        "words": identity.words,
    +                        "admins": identity.admins,
    +                    }
    +                }
    +            )
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    @actor_method
    +    def get_current_identity_name(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.core.identity.me.instance_name})
    +
    +    @actor_method
    +    def set_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            j.core.identity.set_default(identity_instance_name)
    +
    +            return j.data.serializers.json.dumps({"data": {"instance_name": identity_instance_name}})
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    def generate_mnemonic(self) -> str:
    +        words = j.data.encryption.generate_mnemonic()
    +        return j.data.serializers.json.dumps({"data": words})
    +
    +    @actor_method
    +    def check_tname_exists(self, tname) -> str:
    +        try:
    +            j.core.identity.get_user(tname)
    +            return j.data.serializers.json.dumps({"data": True})
    +        except j.exceptions.NotFound:
    +            return j.data.serializers.json.dumps({"data": False})
    +
    +    @actor_method
    +    def check_identity_instance_name(self, name) -> str:
    +        if name in j.core.identity.list_all():
    +            return j.data.serializers.json.dumps({"data": True})
    +        return j.data.serializers.json.dumps({"data": False})
    +
    +    @actor_method
    +    def add_identity(
    +        self,
    +        display_name: str,
    +        tname: str,
    +        email: str,
    +        words: str,
    +        admins: list,
    +    ) -> str:
    +        checked_admins = []
    +        for admin in admins:
    +            if type(admin) is str and not admin.endswith(".3bot"):
    +                admin = admin + ".3bot"
    +            checked_admins.append(admin)
    +
    +        if not display_name.isidentifier() or not display_name.islower():
    +            raise j.exceptions.Value(
    +                "The display name must be a lowercase valid python identitifier (English letters, underscores, and numbers not starting with a number)."
    +            )
    +        identity_instance_name = display_name
    +        if identity_instance_name in j.core.identity.list_all():
    +            raise j.exceptions.Value("Identity with the same name already exists")
    +
    +        try:
    +            new_identity = j.core.identity.new(
    +                name=identity_instance_name,
    +                tname=tname,
    +                email=email,
    +                words=words,
    +                admins=checked_admins,
    +            )
    +            new_identity.register()
    +            new_identity.save()
    +        except HTTPError as e:
    +            j.core.identity.delete(identity_instance_name)
    +            try:
    +                raise j.exceptions.Value(j.data.serializers.json.loads(e.response.content)["error"])
    +            except (KeyError, json.decoder.JSONDecodeError):
    +                # it failed for some reason other than Bad Request error
    +                raise j.exceptions.Value(str(e))
    +        except Exception as e:
    +            # register sometimes throws exceptions other than HTTP like Input
    +            j.core.identity.delete(identity_instance_name)
    +            raise j.exceptions.Value(str(e))
    +        return j.data.serializers.json.dumps({"data": "New identity successfully created and registered"})
    +
    +    @actor_method
    +    def get_config(self) -> str:
    +        config_obj = j.core.config.get_config()
    +        return j.data.serializers.json.dumps({"data": config_obj})
    +
    +    @actor_method
    +    def get_sdk_version(self) -> str:
    +        import importlib_metadata as metadata
    +
    +        packages = ["js-ng", "js-sdk"]
    +        data = {}
    +        for package in packages:
    +            data[package] = metadata.version(package)
    +        return j.data.serializers.json.dumps({"data": data})
    +
    +    @actor_method
    +    def delete_identity(self, identity_instance_name: str) -> str:
    +        identity_names = j.core.identity.list_all()
    +        if identity_instance_name in identity_names:
    +            identity = j.core.identity.get(identity_instance_name)
    +            if identity.instance_name == j.core.identity.me.instance_name:
    +                return j.data.serializers.json.dumps({"data": "Cannot delete current default identity"})
    +
    +            j.core.identity.delete(identity_instance_name)
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} deleted successfully"})
    +        else:
    +            return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +    @actor_method
    +    def list_sshkeys(self) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def add_sshkey(self, key_id, sshkey) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        if key_id not in sshkeys:
    +            sshkeys[key_id] = sshkey
    +            j.core.config.set("SSH_KEYS", sshkeys)
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def delete_sshkey(self, key_id) -> str:
    +        sshkeys = j.core.config.get("SSH_KEYS", {})
    +        if key_id in sshkeys:
    +            sshkeys.pop(key_id)
    +            j.core.config.set("SSH_KEYS", sshkeys)
    +        return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +    @actor_method
    +    def get_developer_options(self) -> str:
    +        test_cert = j.core.config.set_default("TEST_CERT", False)
    +        over_provision = j.core.config.set_default("OVER_PROVISIONING", False)
    +        escalation_emails = j.core.config.set_default("ESCALATION_EMAILS_ENABLED", False)
    +        sort_nodes_by_sru = j.core.config.set_default("SORT_NODES_BY_SRU", False)
    +        return j.data.serializers.json.dumps(
    +            {
    +                "data": {
    +                    "test_cert": test_cert,
    +                    "over_provision": over_provision,
    +                    "escalation_emails": escalation_emails,
    +                    "sort_nodes_by_sru": sort_nodes_by_sru,
    +                }
    +            }
    +        )
    +
    +    @actor_method
    +    def set_developer_options(
    +        self,
    +        test_cert: bool,
    +        over_provision: bool,
    +        sort_nodes_by_sru: bool,
    +        escalation_emails: bool,
    +    ) -> str:
    +        j.core.config.set("TEST_CERT", test_cert)
    +        j.core.config.set("OVER_PROVISIONING", over_provision)
    +        j.core.config.set("ESCALATION_EMAILS_ENABLED", escalation_emails)
    +        j.core.config.set("SORT_NODES_BY_SRU", sort_nodes_by_sru)
    +        return j.data.serializers.json.dumps(
    +            {
    +                "data": {
    +                    "test_cert": test_cert,
    +                    "over_provision": over_provision,
    +                    "escalation_emails": escalation_emails,
    +                    "sort_nodes_by_sru": sort_nodes_by_sru,
    +                }
    +            }
    +        )
    +
    +    @actor_method
    +    def clear_blocked_nodes(self) -> str:
    +        j.sals.reservation_chatflow.reservation_chatflow.clear_blocked_nodes()
    +        return j.data.serializers.json.dumps({"data": "blocked nodes got cleared successfully."})
    +
    +    @actor_method
    +    def clear_blocked_managed_domains(self) -> str:
    +        j.sals.marketplace.deployer.clear_blocked_managed_domains()
    +        return j.data.serializers.json.dumps({"data": "blocked managed domains got cleared successfully."})
    +
    +    @actor_method
    +    def get_email_server_config(self) -> str:
    +        email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +        email_server_config.setdefault("host", "")
    +        email_server_config.setdefault("port", "")
    +        email_server_config.setdefault("username", "")
    +        email_server_config.setdefault("password", "")
    +        return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +    @actor_method
    +    def set_email_server_config(self, host="", port="", username="", password="") -> str:
    +        email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +        email_server_config = {"host": host, "port": port, "username": username, "password": password}
    +        j.core.config.set("EMAIL_SERVER_CONFIG", email_server_config)
    +        return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +    @actor_method
    +    def list_escalation_emails(self) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    @actor_method
    +    def add_escalation_email(self, email) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        if email not in escalation_emails:
    +            escalation_emails.append(email)
    +            j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    @actor_method
    +    def delete_escalation_email(self, email) -> str:
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +        if email in escalation_emails:
    +            escalation_emails.remove(email)
    +            j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +        return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +    def get_notifications(self) -> str:
    +        notifications = []
    +        if j.tools.notificationsqueue.count() >= 10:
    +            notifications = j.tools.notificationsqueue.fetch()
    +        else:
    +            notifications = j.tools.notificationsqueue.fetch(10)
    +        ret = [notification.json for notification in notifications]
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def get_notifications_count(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.tools.notificationsqueue.count()})
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def add_admin(self, name: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_admin(self, name: str):
    +    if name in j.core.identity.me.admins:
    +        raise j.exceptions.Value(f"Admin {name} already exists")
    +    j.core.identity.me.admins.append(name)
    +    j.core.identity.me.save()
    +
    +
    +
    +def add_escalation_email(self, email) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_escalation_email(self, email) -> str:
    +    escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +    if email not in escalation_emails:
    +        escalation_emails.append(email)
    +        j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +    return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +
    +
    +def add_identity(self, display_name: str, tname: str, email: str, words: str, admins: list) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_identity(
    +    self,
    +    display_name: str,
    +    tname: str,
    +    email: str,
    +    words: str,
    +    admins: list,
    +) -> str:
    +    checked_admins = []
    +    for admin in admins:
    +        if type(admin) is str and not admin.endswith(".3bot"):
    +            admin = admin + ".3bot"
    +        checked_admins.append(admin)
    +
    +    if not display_name.isidentifier() or not display_name.islower():
    +        raise j.exceptions.Value(
    +            "The display name must be a lowercase valid python identitifier (English letters, underscores, and numbers not starting with a number)."
    +        )
    +    identity_instance_name = display_name
    +    if identity_instance_name in j.core.identity.list_all():
    +        raise j.exceptions.Value("Identity with the same name already exists")
    +
    +    try:
    +        new_identity = j.core.identity.new(
    +            name=identity_instance_name,
    +            tname=tname,
    +            email=email,
    +            words=words,
    +            admins=checked_admins,
    +        )
    +        new_identity.register()
    +        new_identity.save()
    +    except HTTPError as e:
    +        j.core.identity.delete(identity_instance_name)
    +        try:
    +            raise j.exceptions.Value(j.data.serializers.json.loads(e.response.content)["error"])
    +        except (KeyError, json.decoder.JSONDecodeError):
    +            # it failed for some reason other than Bad Request error
    +            raise j.exceptions.Value(str(e))
    +    except Exception as e:
    +        # register sometimes throws exceptions other than HTTP like Input
    +        j.core.identity.delete(identity_instance_name)
    +        raise j.exceptions.Value(str(e))
    +    return j.data.serializers.json.dumps({"data": "New identity successfully created and registered"})
    +
    +
    +
    +def add_sshkey(self, key_id, sshkey) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_sshkey(self, key_id, sshkey) -> str:
    +    sshkeys = j.core.config.get("SSH_KEYS", {})
    +    if key_id not in sshkeys:
    +        sshkeys[key_id] = sshkey
    +        j.core.config.set("SSH_KEYS", sshkeys)
    +    return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +
    +
    +def check_identity_instance_name(self, name) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def check_identity_instance_name(self, name) -> str:
    +    if name in j.core.identity.list_all():
    +        return j.data.serializers.json.dumps({"data": True})
    +    return j.data.serializers.json.dumps({"data": False})
    +
    +
    +
    +def check_tname_exists(self, tname) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def check_tname_exists(self, tname) -> str:
    +    try:
    +        j.core.identity.get_user(tname)
    +        return j.data.serializers.json.dumps({"data": True})
    +    except j.exceptions.NotFound:
    +        return j.data.serializers.json.dumps({"data": False})
    +
    +
    +
    +def clear_blocked_managed_domains(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def clear_blocked_managed_domains(self) -> str:
    +    j.sals.marketplace.deployer.clear_blocked_managed_domains()
    +    return j.data.serializers.json.dumps({"data": "blocked managed domains got cleared successfully."})
    +
    +
    +
    +def clear_blocked_nodes(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def clear_blocked_nodes(self) -> str:
    +    j.sals.reservation_chatflow.reservation_chatflow.clear_blocked_nodes()
    +    return j.data.serializers.json.dumps({"data": "blocked nodes got cleared successfully."})
    +
    +
    +
    +def delete_admin(self, name: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_admin(self, name: str):
    +    if name not in j.core.identity.me.admins:
    +        raise j.exceptions.Value(f"Admin {name} does not exist")
    +    j.core.identity.me.admins.remove(name)
    +    j.core.identity.me.save()
    +
    +
    +
    +def delete_escalation_email(self, email) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_escalation_email(self, email) -> str:
    +    escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +    if email in escalation_emails:
    +        escalation_emails.remove(email)
    +        j.core.config.set("ESCALATION_EMAILS", escalation_emails)
    +    return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +
    +
    +def delete_identity(self, identity_instance_name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_identity(self, identity_instance_name: str) -> str:
    +    identity_names = j.core.identity.list_all()
    +    if identity_instance_name in identity_names:
    +        identity = j.core.identity.get(identity_instance_name)
    +        if identity.instance_name == j.core.identity.me.instance_name:
    +            return j.data.serializers.json.dumps({"data": "Cannot delete current default identity"})
    +
    +        j.core.identity.delete(identity_instance_name)
    +        return j.data.serializers.json.dumps({"data": f"{identity_instance_name} deleted successfully"})
    +    else:
    +        return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +
    +
    +def delete_sshkey(self, key_id) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_sshkey(self, key_id) -> str:
    +    sshkeys = j.core.config.get("SSH_KEYS", {})
    +    if key_id in sshkeys:
    +        sshkeys.pop(key_id)
    +        j.core.config.set("SSH_KEYS", sshkeys)
    +    return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +
    +
    +def generate_mnemonic(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    def generate_mnemonic(self) -> str:
    +    words = j.data.encryption.generate_mnemonic()
    +    return j.data.serializers.json.dumps({"data": words})
    +
    +
    +
    +def get_config(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_config(self) -> str:
    +    config_obj = j.core.config.get_config()
    +    return j.data.serializers.json.dumps({"data": config_obj})
    +
    +
    +
    +def get_current_identity_name(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_current_identity_name(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.core.identity.me.instance_name})
    +
    +
    +
    +def get_developer_options(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_developer_options(self) -> str:
    +    test_cert = j.core.config.set_default("TEST_CERT", False)
    +    over_provision = j.core.config.set_default("OVER_PROVISIONING", False)
    +    escalation_emails = j.core.config.set_default("ESCALATION_EMAILS_ENABLED", False)
    +    sort_nodes_by_sru = j.core.config.set_default("SORT_NODES_BY_SRU", False)
    +    return j.data.serializers.json.dumps(
    +        {
    +            "data": {
    +                "test_cert": test_cert,
    +                "over_provision": over_provision,
    +                "escalation_emails": escalation_emails,
    +                "sort_nodes_by_sru": sort_nodes_by_sru,
    +            }
    +        }
    +    )
    +
    +
    +
    +def get_email_server_config(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_email_server_config(self) -> str:
    +    email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +    email_server_config.setdefault("host", "")
    +    email_server_config.setdefault("port", "")
    +    email_server_config.setdefault("username", "")
    +    email_server_config.setdefault("password", "")
    +    return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +
    +
    +def get_identity(self, identity_instance_name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_identity(self, identity_instance_name: str) -> str:
    +    identity_names = j.core.identity.list_all()
    +    if identity_instance_name in identity_names:
    +        identity = j.core.identity.get(identity_instance_name)
    +
    +        return j.data.serializers.json.dumps(
    +            {
    +                "data": {
    +                    "instance_name": identity.instance_name,
    +                    "name": identity.tname,
    +                    "email": identity.email,
    +                    "tid": identity.tid,
    +                    "words": identity.words,
    +                    "admins": identity.admins,
    +                }
    +            }
    +        )
    +    else:
    +        return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +
    +
    +def get_notifications(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    def get_notifications(self) -> str:
    +    notifications = []
    +    if j.tools.notificationsqueue.count() >= 10:
    +        notifications = j.tools.notificationsqueue.fetch()
    +    else:
    +        notifications = j.tools.notificationsqueue.fetch(10)
    +    ret = [notification.json for notification in notifications]
    +    return j.data.serializers.json.dumps({"data": ret})
    +
    +
    +
    +def get_notifications_count(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_notifications_count(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.tools.notificationsqueue.count()})
    +
    +
    +
    +def get_sdk_version(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_sdk_version(self) -> str:
    +    import importlib_metadata as metadata
    +
    +    packages = ["js-ng", "js-sdk"]
    +    data = {}
    +    for package in packages:
    +        data[package] = metadata.version(package)
    +    return j.data.serializers.json.dumps({"data": data})
    +
    +
    +
    +def list_admins(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_admins(self) -> str:
    +    admins = list(set(j.core.identity.me.admins))
    +    return j.data.serializers.json.dumps({"data": admins})
    +
    +
    +
    +def list_escalation_emails(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_escalation_emails(self) -> str:
    +    escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +    return j.data.serializers.json.dumps({"data": escalation_emails})
    +
    +
    +
    +def list_identities(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_identities(self) -> str:
    +    identities = j.core.identity.list_all()
    +    identity_data = {}
    +    for identity_name in identities:
    +        try:
    +            identity = j.core.identity.get(identity_name)
    +            if identity.tid < 0:
    +                continue
    +            identity_dict = identity.to_dict()
    +            identity_dict["instance_name"] = identity.instance_name
    +            identity_dict.pop("__words")
    +        except Exception as e:
    +            j.logger.exception("error", exception=e)
    +            # TODO: include traceback
    +            j.tools.alerthandler.alert_raise(
    +                app_name="admin",
    +                category="internal_errors",
    +                message=f"failed to get identity {identity_name} info due to error {str(e)}",
    +                alert_type="exception",
    +            )
    +            continue
    +        identity_data[identity_name] = identity_dict
    +    return j.data.serializers.json.dumps({"data": identity_data})
    +
    +
    +
    +def list_sshkeys(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_sshkeys(self) -> str:
    +    sshkeys = j.core.config.get("SSH_KEYS", {})
    +    return j.data.serializers.json.dumps({"data": sshkeys})
    +
    +
    +
    +def set_developer_options(self, test_cert: bool, over_provision: bool, sort_nodes_by_sru: bool, escalation_emails: bool) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def set_developer_options(
    +    self,
    +    test_cert: bool,
    +    over_provision: bool,
    +    sort_nodes_by_sru: bool,
    +    escalation_emails: bool,
    +) -> str:
    +    j.core.config.set("TEST_CERT", test_cert)
    +    j.core.config.set("OVER_PROVISIONING", over_provision)
    +    j.core.config.set("ESCALATION_EMAILS_ENABLED", escalation_emails)
    +    j.core.config.set("SORT_NODES_BY_SRU", sort_nodes_by_sru)
    +    return j.data.serializers.json.dumps(
    +        {
    +            "data": {
    +                "test_cert": test_cert,
    +                "over_provision": over_provision,
    +                "escalation_emails": escalation_emails,
    +                "sort_nodes_by_sru": sort_nodes_by_sru,
    +            }
    +        }
    +    )
    +
    +
    +
    +def set_email_server_config(self, host='', port='', username='', password='') ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def set_email_server_config(self, host="", port="", username="", password="") -> str:
    +    email_server_config = j.core.config.get("EMAIL_SERVER_CONFIG", {})
    +    email_server_config = {"host": host, "port": port, "username": username, "password": password}
    +    j.core.config.set("EMAIL_SERVER_CONFIG", email_server_config)
    +    return j.data.serializers.json.dumps({"data": email_server_config})
    +
    +
    +
    +def set_identity(self, identity_instance_name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def set_identity(self, identity_instance_name: str) -> str:
    +    identity_names = j.core.identity.list_all()
    +    if identity_instance_name in identity_names:
    +        j.core.identity.set_default(identity_instance_name)
    +
    +        return j.data.serializers.json.dumps({"data": {"instance_name": identity_instance_name}})
    +    else:
    +        return j.data.serializers.json.dumps({"data": f"{identity_instance_name} doesn't exist"})
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/actors/alerts.html b/docs/api/jumpscale/packages/admin/actors/alerts.html new file mode 100644 index 000000000..5670a3585 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/actors/alerts.html @@ -0,0 +1,396 @@ + + + + + + +jumpscale.packages.admin.actors.alerts API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.actors.alerts

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +
    +
    +class Alerts(BaseActor):
    +    @actor_method
    +    def list_alerts(self) -> str:
    +        """
    +            get all alerts
    +        """
    +        ret = [alert.json for alert in j.tools.alerthandler.find()]
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def get_alerts_count(self) -> str:
    +        """
    +            get count of alerts
    +        """
    +        return j.data.serializers.json.dumps({"data": j.tools.alerthandler.count()})
    +
    +    @actor_method
    +    def delete_alerts(self, ids: list = []) -> str:
    +        """
    +            delete list of alerts
    +        """
    +        try:
    +            if ids:
    +                for _id in ids:
    +                    j.tools.alerthandler.delete(_id)
    +            return j.data.serializers.json.dumps({"data": "success"})
    +        except:
    +            raise j.exceptions.Value("Error in delete alerts")
    +
    +    @actor_method
    +    def delete_all_alerts(self):
    +        """
    +            delete all alerts
    +        """
    +        try:
    +            j.tools.alerthandler.delete_all()
    +        except Exception as e:
    +            raise e
    +
    +
    +Actor = Alerts
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Alerts +
    +
    +
    +
    + +Expand source code + +
    class Alerts(BaseActor):
    +    @actor_method
    +    def list_alerts(self) -> str:
    +        """
    +            get all alerts
    +        """
    +        ret = [alert.json for alert in j.tools.alerthandler.find()]
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def get_alerts_count(self) -> str:
    +        """
    +            get count of alerts
    +        """
    +        return j.data.serializers.json.dumps({"data": j.tools.alerthandler.count()})
    +
    +    @actor_method
    +    def delete_alerts(self, ids: list = []) -> str:
    +        """
    +            delete list of alerts
    +        """
    +        try:
    +            if ids:
    +                for _id in ids:
    +                    j.tools.alerthandler.delete(_id)
    +            return j.data.serializers.json.dumps({"data": "success"})
    +        except:
    +            raise j.exceptions.Value("Error in delete alerts")
    +
    +    @actor_method
    +    def delete_all_alerts(self):
    +        """
    +            delete all alerts
    +        """
    +        try:
    +            j.tools.alerthandler.delete_all()
    +        except Exception as e:
    +            raise e
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def delete_alerts(self, ids: list = []) ‑> str +
    +
    +

    delete list of alerts

    +
    + +Expand source code + +
    @actor_method
    +def delete_alerts(self, ids: list = []) -> str:
    +    """
    +        delete list of alerts
    +    """
    +    try:
    +        if ids:
    +            for _id in ids:
    +                j.tools.alerthandler.delete(_id)
    +        return j.data.serializers.json.dumps({"data": "success"})
    +    except:
    +        raise j.exceptions.Value("Error in delete alerts")
    +
    +
    +
    +def delete_all_alerts(self) +
    +
    +

    delete all alerts

    +
    + +Expand source code + +
    @actor_method
    +def delete_all_alerts(self):
    +    """
    +        delete all alerts
    +    """
    +    try:
    +        j.tools.alerthandler.delete_all()
    +    except Exception as e:
    +        raise e
    +
    +
    +
    +def get_alerts_count(self) ‑> str +
    +
    +

    get count of alerts

    +
    + +Expand source code + +
    @actor_method
    +def get_alerts_count(self) -> str:
    +    """
    +        get count of alerts
    +    """
    +    return j.data.serializers.json.dumps({"data": j.tools.alerthandler.count()})
    +
    +
    +
    +def list_alerts(self) ‑> str +
    +
    +

    get all alerts

    +
    + +Expand source code + +
    @actor_method
    +def list_alerts(self) -> str:
    +    """
    +        get all alerts
    +    """
    +    ret = [alert.json for alert in j.tools.alerthandler.find()]
    +    return j.data.serializers.json.dumps({"data": ret})
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class Alerts(BaseActor):
    +    @actor_method
    +    def list_alerts(self) -> str:
    +        """
    +            get all alerts
    +        """
    +        ret = [alert.json for alert in j.tools.alerthandler.find()]
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def get_alerts_count(self) -> str:
    +        """
    +            get count of alerts
    +        """
    +        return j.data.serializers.json.dumps({"data": j.tools.alerthandler.count()})
    +
    +    @actor_method
    +    def delete_alerts(self, ids: list = []) -> str:
    +        """
    +            delete list of alerts
    +        """
    +        try:
    +            if ids:
    +                for _id in ids:
    +                    j.tools.alerthandler.delete(_id)
    +            return j.data.serializers.json.dumps({"data": "success"})
    +        except:
    +            raise j.exceptions.Value("Error in delete alerts")
    +
    +    @actor_method
    +    def delete_all_alerts(self):
    +        """
    +            delete all alerts
    +        """
    +        try:
    +            j.tools.alerthandler.delete_all()
    +        except Exception as e:
    +            raise e
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def delete_alerts(self, ids: list = []) ‑> str +
    +
    +

    delete list of alerts

    +
    + +Expand source code + +
    @actor_method
    +def delete_alerts(self, ids: list = []) -> str:
    +    """
    +        delete list of alerts
    +    """
    +    try:
    +        if ids:
    +            for _id in ids:
    +                j.tools.alerthandler.delete(_id)
    +        return j.data.serializers.json.dumps({"data": "success"})
    +    except:
    +        raise j.exceptions.Value("Error in delete alerts")
    +
    +
    +
    +def delete_all_alerts(self) +
    +
    +

    delete all alerts

    +
    + +Expand source code + +
    @actor_method
    +def delete_all_alerts(self):
    +    """
    +        delete all alerts
    +    """
    +    try:
    +        j.tools.alerthandler.delete_all()
    +    except Exception as e:
    +        raise e
    +
    +
    +
    +def get_alerts_count(self) ‑> str +
    +
    +

    get count of alerts

    +
    + +Expand source code + +
    @actor_method
    +def get_alerts_count(self) -> str:
    +    """
    +        get count of alerts
    +    """
    +    return j.data.serializers.json.dumps({"data": j.tools.alerthandler.count()})
    +
    +
    +
    +def list_alerts(self) ‑> str +
    +
    +

    get all alerts

    +
    + +Expand source code + +
    @actor_method
    +def list_alerts(self) -> str:
    +    """
    +        get all alerts
    +    """
    +    ret = [alert.json for alert in j.tools.alerthandler.find()]
    +    return j.data.serializers.json.dumps({"data": ret})
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/actors/health.html b/docs/api/jumpscale/packages/admin/actors/health.html new file mode 100644 index 000000000..44ae210ce --- /dev/null +++ b/docs/api/jumpscale/packages/admin/actors/health.html @@ -0,0 +1,554 @@ + + + + + + +jumpscale.packages.admin.actors.health API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.actors.health

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +
    +from jumpscale.clients.stellar import HORIZON_NETWORKS, THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES
    +
    +
    +class Health(BaseActor):
    +    @actor_method
    +    def get_disk_space(self) -> str:
    +        res = {}
    +        disk_obj = j.sals.fs.shutil.disk_usage("/")
    +        res["total"] = disk_obj.total // (1024.0**3)
    +        res["used"] = disk_obj.used // (1024.0**3)
    +        res["free"] = disk_obj.free // (1024.0**3)
    +        res["percent"] = (res["used"] / res["total"]) * 100
    +        return j.data.serializers.json.dumps({"data": res})
    +
    +    @actor_method
    +    def health(self) -> str:
    +        return "All is good"
    +
    +    @actor_method
    +    def network_info(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.nettools.get_default_ip_config()})
    +
    +    @actor_method
    +    def js_version(self) -> str:
    +        #  TODO: add version actor
    +        return "need to add version actor"
    +
    +    @actor_method
    +    def get_memory_usage(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.process.get_memory_usage()})
    +
    +    @actor_method
    +    def get_running_processes(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.process.get_processes_info()})
    +
    +    @actor_method
    +    def get_health_checks(self, network="STD") -> str:
    +        services = {
    +            "stellar": {"name": "Stellar", "status": True},
    +            "token_services": {"name": "Token Services", "status": True},
    +        }
    +
    +        # urls of services according to network
    +        stellar_url = HORIZON_NETWORKS[network]
    +        tokenservices_url = THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[network]
    +
    +        # check stellar service
    +        try:
    +            j.tools.http.get(stellar_url)
    +        except:
    +            services["stellar"]["status"] = False
    +
    +        # check token services
    +        try:
    +            j.tools.http.get(tokenservices_url)
    +        except:
    +            services["token_services"]["status"] = False
    +
    +        return j.data.serializers.json.dumps({"data": services})
    +
    +
    +Actor = Health
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Health +
    +
    +
    +
    + +Expand source code + +
    class Health(BaseActor):
    +    @actor_method
    +    def get_disk_space(self) -> str:
    +        res = {}
    +        disk_obj = j.sals.fs.shutil.disk_usage("/")
    +        res["total"] = disk_obj.total // (1024.0**3)
    +        res["used"] = disk_obj.used // (1024.0**3)
    +        res["free"] = disk_obj.free // (1024.0**3)
    +        res["percent"] = (res["used"] / res["total"]) * 100
    +        return j.data.serializers.json.dumps({"data": res})
    +
    +    @actor_method
    +    def health(self) -> str:
    +        return "All is good"
    +
    +    @actor_method
    +    def network_info(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.nettools.get_default_ip_config()})
    +
    +    @actor_method
    +    def js_version(self) -> str:
    +        #  TODO: add version actor
    +        return "need to add version actor"
    +
    +    @actor_method
    +    def get_memory_usage(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.process.get_memory_usage()})
    +
    +    @actor_method
    +    def get_running_processes(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.process.get_processes_info()})
    +
    +    @actor_method
    +    def get_health_checks(self, network="STD") -> str:
    +        services = {
    +            "stellar": {"name": "Stellar", "status": True},
    +            "token_services": {"name": "Token Services", "status": True},
    +        }
    +
    +        # urls of services according to network
    +        stellar_url = HORIZON_NETWORKS[network]
    +        tokenservices_url = THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[network]
    +
    +        # check stellar service
    +        try:
    +            j.tools.http.get(stellar_url)
    +        except:
    +            services["stellar"]["status"] = False
    +
    +        # check token services
    +        try:
    +            j.tools.http.get(tokenservices_url)
    +        except:
    +            services["token_services"]["status"] = False
    +
    +        return j.data.serializers.json.dumps({"data": services})
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def get_disk_space(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_disk_space(self) -> str:
    +    res = {}
    +    disk_obj = j.sals.fs.shutil.disk_usage("/")
    +    res["total"] = disk_obj.total // (1024.0**3)
    +    res["used"] = disk_obj.used // (1024.0**3)
    +    res["free"] = disk_obj.free // (1024.0**3)
    +    res["percent"] = (res["used"] / res["total"]) * 100
    +    return j.data.serializers.json.dumps({"data": res})
    +
    +
    +
    +def get_health_checks(self, network='STD') ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_health_checks(self, network="STD") -> str:
    +    services = {
    +        "stellar": {"name": "Stellar", "status": True},
    +        "token_services": {"name": "Token Services", "status": True},
    +    }
    +
    +    # urls of services according to network
    +    stellar_url = HORIZON_NETWORKS[network]
    +    tokenservices_url = THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[network]
    +
    +    # check stellar service
    +    try:
    +        j.tools.http.get(stellar_url)
    +    except:
    +        services["stellar"]["status"] = False
    +
    +    # check token services
    +    try:
    +        j.tools.http.get(tokenservices_url)
    +    except:
    +        services["token_services"]["status"] = False
    +
    +    return j.data.serializers.json.dumps({"data": services})
    +
    +
    +
    +def get_memory_usage(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_memory_usage(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.sals.process.get_memory_usage()})
    +
    +
    +
    +def get_running_processes(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_running_processes(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.sals.process.get_processes_info()})
    +
    +
    +
    +def health(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def health(self) -> str:
    +    return "All is good"
    +
    +
    +
    +def js_version(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def js_version(self) -> str:
    +    #  TODO: add version actor
    +    return "need to add version actor"
    +
    +
    +
    +def network_info(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def network_info(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.sals.nettools.get_default_ip_config()})
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class Health(BaseActor):
    +    @actor_method
    +    def get_disk_space(self) -> str:
    +        res = {}
    +        disk_obj = j.sals.fs.shutil.disk_usage("/")
    +        res["total"] = disk_obj.total // (1024.0**3)
    +        res["used"] = disk_obj.used // (1024.0**3)
    +        res["free"] = disk_obj.free // (1024.0**3)
    +        res["percent"] = (res["used"] / res["total"]) * 100
    +        return j.data.serializers.json.dumps({"data": res})
    +
    +    @actor_method
    +    def health(self) -> str:
    +        return "All is good"
    +
    +    @actor_method
    +    def network_info(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.nettools.get_default_ip_config()})
    +
    +    @actor_method
    +    def js_version(self) -> str:
    +        #  TODO: add version actor
    +        return "need to add version actor"
    +
    +    @actor_method
    +    def get_memory_usage(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.process.get_memory_usage()})
    +
    +    @actor_method
    +    def get_running_processes(self) -> str:
    +        return j.data.serializers.json.dumps({"data": j.sals.process.get_processes_info()})
    +
    +    @actor_method
    +    def get_health_checks(self, network="STD") -> str:
    +        services = {
    +            "stellar": {"name": "Stellar", "status": True},
    +            "token_services": {"name": "Token Services", "status": True},
    +        }
    +
    +        # urls of services according to network
    +        stellar_url = HORIZON_NETWORKS[network]
    +        tokenservices_url = THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[network]
    +
    +        # check stellar service
    +        try:
    +            j.tools.http.get(stellar_url)
    +        except:
    +            services["stellar"]["status"] = False
    +
    +        # check token services
    +        try:
    +            j.tools.http.get(tokenservices_url)
    +        except:
    +            services["token_services"]["status"] = False
    +
    +        return j.data.serializers.json.dumps({"data": services})
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def get_disk_space(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_disk_space(self) -> str:
    +    res = {}
    +    disk_obj = j.sals.fs.shutil.disk_usage("/")
    +    res["total"] = disk_obj.total // (1024.0**3)
    +    res["used"] = disk_obj.used // (1024.0**3)
    +    res["free"] = disk_obj.free // (1024.0**3)
    +    res["percent"] = (res["used"] / res["total"]) * 100
    +    return j.data.serializers.json.dumps({"data": res})
    +
    +
    +
    +def get_health_checks(self, network='STD') ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_health_checks(self, network="STD") -> str:
    +    services = {
    +        "stellar": {"name": "Stellar", "status": True},
    +        "token_services": {"name": "Token Services", "status": True},
    +    }
    +
    +    # urls of services according to network
    +    stellar_url = HORIZON_NETWORKS[network]
    +    tokenservices_url = THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[network]
    +
    +    # check stellar service
    +    try:
    +        j.tools.http.get(stellar_url)
    +    except:
    +        services["stellar"]["status"] = False
    +
    +    # check token services
    +    try:
    +        j.tools.http.get(tokenservices_url)
    +    except:
    +        services["token_services"]["status"] = False
    +
    +    return j.data.serializers.json.dumps({"data": services})
    +
    +
    +
    +def get_memory_usage(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_memory_usage(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.sals.process.get_memory_usage()})
    +
    +
    +
    +def get_running_processes(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_running_processes(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.sals.process.get_processes_info()})
    +
    +
    +
    +def health(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def health(self) -> str:
    +    return "All is good"
    +
    +
    +
    +def js_version(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def js_version(self) -> str:
    +    #  TODO: add version actor
    +    return "need to add version actor"
    +
    +
    +
    +def network_info(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def network_info(self) -> str:
    +    return j.data.serializers.json.dumps({"data": j.sals.nettools.get_default_ip_config()})
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/actors/identity.html b/docs/api/jumpscale/packages/admin/actors/identity.html new file mode 100644 index 000000000..1dea76952 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/actors/identity.html @@ -0,0 +1,311 @@ + + + + + + +jumpscale.packages.admin.actors.identity API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.actors.identity

    +
    +
    +
    + +Expand source code + +
    import os
    +from urllib.parse import urlparse
    +from jumpscale.loader import j
    +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +
    +
    +class Identity(BaseActor):
    +    @actor_method
    +    def get_identity(self) -> str:
    +        data = None
    +        if j.core.identity.list_all():
    +            data = {
    +                "id": j.core.identity.me.tid,
    +                "name": j.core.identity.me.tname,
    +                "email": j.core.identity.me.email,
    +            }
    +        return j.data.serializers.json.dumps(data)
    +
    +    @actor_method
    +    def set_identity(self, label: str, tname: str, email: str, words: str):
    +        j.core.identity.get(label, tname=tname, email=email, words=words)
    +        j.core.identity.set_default(label)
    +        j.core.config.set("threebot_connect", True)
    +
    +    @actor_method
    +    def list_identities(self) -> str:
    +        identities = {}
    +        for label in j.core.identity.list_all():
    +            identity = j.core.identity.get(label)
    +            identities[label] = {"name": identity.tname, "email": identity.email}
    +        return j.data.serializers.json.dumps(identities)
    +
    +
    +Actor = Identity
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Identity +
    +
    +
    +
    + +Expand source code + +
    class Identity(BaseActor):
    +    @actor_method
    +    def get_identity(self) -> str:
    +        data = None
    +        if j.core.identity.list_all():
    +            data = {
    +                "id": j.core.identity.me.tid,
    +                "name": j.core.identity.me.tname,
    +                "email": j.core.identity.me.email,
    +            }
    +        return j.data.serializers.json.dumps(data)
    +
    +    @actor_method
    +    def set_identity(self, label: str, tname: str, email: str, words: str):
    +        j.core.identity.get(label, tname=tname, email=email, words=words)
    +        j.core.identity.set_default(label)
    +        j.core.config.set("threebot_connect", True)
    +
    +    @actor_method
    +    def list_identities(self) -> str:
    +        identities = {}
    +        for label in j.core.identity.list_all():
    +            identity = j.core.identity.get(label)
    +            identities[label] = {"name": identity.tname, "email": identity.email}
    +        return j.data.serializers.json.dumps(identities)
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def get_identity(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_identity(self) -> str:
    +    data = None
    +    if j.core.identity.list_all():
    +        data = {
    +            "id": j.core.identity.me.tid,
    +            "name": j.core.identity.me.tname,
    +            "email": j.core.identity.me.email,
    +        }
    +    return j.data.serializers.json.dumps(data)
    +
    +
    +
    +def list_identities(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_identities(self) -> str:
    +    identities = {}
    +    for label in j.core.identity.list_all():
    +        identity = j.core.identity.get(label)
    +        identities[label] = {"name": identity.tname, "email": identity.email}
    +    return j.data.serializers.json.dumps(identities)
    +
    +
    +
    +def set_identity(self, label: str, tname: str, email: str, words: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def set_identity(self, label: str, tname: str, email: str, words: str):
    +    j.core.identity.get(label, tname=tname, email=email, words=words)
    +    j.core.identity.set_default(label)
    +    j.core.config.set("threebot_connect", True)
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class Identity(BaseActor):
    +    @actor_method
    +    def get_identity(self) -> str:
    +        data = None
    +        if j.core.identity.list_all():
    +            data = {
    +                "id": j.core.identity.me.tid,
    +                "name": j.core.identity.me.tname,
    +                "email": j.core.identity.me.email,
    +            }
    +        return j.data.serializers.json.dumps(data)
    +
    +    @actor_method
    +    def set_identity(self, label: str, tname: str, email: str, words: str):
    +        j.core.identity.get(label, tname=tname, email=email, words=words)
    +        j.core.identity.set_default(label)
    +        j.core.config.set("threebot_connect", True)
    +
    +    @actor_method
    +    def list_identities(self) -> str:
    +        identities = {}
    +        for label in j.core.identity.list_all():
    +            identity = j.core.identity.get(label)
    +            identities[label] = {"name": identity.tname, "email": identity.email}
    +        return j.data.serializers.json.dumps(identities)
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def get_identity(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_identity(self) -> str:
    +    data = None
    +    if j.core.identity.list_all():
    +        data = {
    +            "id": j.core.identity.me.tid,
    +            "name": j.core.identity.me.tname,
    +            "email": j.core.identity.me.email,
    +        }
    +    return j.data.serializers.json.dumps(data)
    +
    +
    +
    +def list_identities(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_identities(self) -> str:
    +    identities = {}
    +    for label in j.core.identity.list_all():
    +        identity = j.core.identity.get(label)
    +        identities[label] = {"name": identity.tname, "email": identity.email}
    +    return j.data.serializers.json.dumps(identities)
    +
    +
    +
    +def set_identity(self, label: str, tname: str, email: str, words: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def set_identity(self, label: str, tname: str, email: str, words: str):
    +    j.core.identity.get(label, tname=tname, email=email, words=words)
    +    j.core.identity.set_default(label)
    +    j.core.config.set("threebot_connect", True)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/actors/index.html b/docs/api/jumpscale/packages/admin/actors/index.html new file mode 100644 index 000000000..160811cb4 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/actors/index.html @@ -0,0 +1,95 @@ + + + + + + +jumpscale.packages.admin.actors API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/actors/logs.html b/docs/api/jumpscale/packages/admin/actors/logs.html new file mode 100644 index 000000000..13e71cd5c --- /dev/null +++ b/docs/api/jumpscale/packages/admin/actors/logs.html @@ -0,0 +1,254 @@ + + + + + + +jumpscale.packages.admin.actors.logs API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.actors.logs

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +
    +
    +class Logs(BaseActor):
    +    @actor_method
    +    def list_apps(self) -> str:
    +        apps = list(j.logger.get_app_names())
    +        return j.data.serializers.json.dumps({"data": apps})
    +
    +    @actor_method
    +    def list_logs(self, app_name: str = j.logger.default_app_name) -> str:
    +        logs = list(j.logger.redis.tail(app_name=app_name))
    +        return j.data.serializers.json.dumps({"data": logs})
    +
    +    @actor_method
    +    def remove_records(self, app_name: str = None):
    +        j.logger.redis.remove_all_records(app_name=app_name)
    +
    +
    +Actor = Logs
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Logs +
    +
    +
    +
    + +Expand source code + +
    class Logs(BaseActor):
    +    @actor_method
    +    def list_apps(self) -> str:
    +        apps = list(j.logger.get_app_names())
    +        return j.data.serializers.json.dumps({"data": apps})
    +
    +    @actor_method
    +    def list_logs(self, app_name: str = j.logger.default_app_name) -> str:
    +        logs = list(j.logger.redis.tail(app_name=app_name))
    +        return j.data.serializers.json.dumps({"data": logs})
    +
    +    @actor_method
    +    def remove_records(self, app_name: str = None):
    +        j.logger.redis.remove_all_records(app_name=app_name)
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def list_apps(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_apps(self) -> str:
    +    apps = list(j.logger.get_app_names())
    +    return j.data.serializers.json.dumps({"data": apps})
    +
    +
    +
    +def list_logs(self, app_name: str = 'init') ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_logs(self, app_name: str = j.logger.default_app_name) -> str:
    +    logs = list(j.logger.redis.tail(app_name=app_name))
    +    return j.data.serializers.json.dumps({"data": logs})
    +
    +
    +
    +def remove_records(self, app_name: str = None) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def remove_records(self, app_name: str = None):
    +    j.logger.redis.remove_all_records(app_name=app_name)
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class Logs(BaseActor):
    +    @actor_method
    +    def list_apps(self) -> str:
    +        apps = list(j.logger.get_app_names())
    +        return j.data.serializers.json.dumps({"data": apps})
    +
    +    @actor_method
    +    def list_logs(self, app_name: str = j.logger.default_app_name) -> str:
    +        logs = list(j.logger.redis.tail(app_name=app_name))
    +        return j.data.serializers.json.dumps({"data": logs})
    +
    +    @actor_method
    +    def remove_records(self, app_name: str = None):
    +        j.logger.redis.remove_all_records(app_name=app_name)
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def list_apps(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_apps(self) -> str:
    +    apps = list(j.logger.get_app_names())
    +    return j.data.serializers.json.dumps({"data": apps})
    +
    +
    +
    +def list_logs(self, app_name: str = 'init') ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_logs(self, app_name: str = j.logger.default_app_name) -> str:
    +    logs = list(j.logger.redis.tail(app_name=app_name))
    +    return j.data.serializers.json.dumps({"data": logs})
    +
    +
    +
    +def remove_records(self, app_name: str = None) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def remove_records(self, app_name: str = None):
    +    j.logger.redis.remove_all_records(app_name=app_name)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/actors/packages.html b/docs/api/jumpscale/packages/admin/actors/packages.html new file mode 100644 index 000000000..eb6e38a58 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/actors/packages.html @@ -0,0 +1,677 @@ + + + + + + +jumpscale.packages.admin.actors.packages API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.actors.packages

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +from jumpscale.core.exceptions import JSException
    +
    +
    +class Packages(BaseActor):
    +    def __init__(self):
    +        self._threebot = None
    +
    +    @property
    +    def threebot(self):
    +        if not self._threebot:
    +            self._threebot = j.servers.threebot.get()
    +        return self._threebot
    +
    +    @actor_method
    +    def get_package_status(self, names: list) -> str:
    +        return "hello from packages_get_status actor"
    +
    +    @actor_method
    +    def list_packages(self) -> str:
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.get_packages()})
    +
    +    @actor_method
    +    def packages_names(self) -> str:
    +        return j.data.serializers.json.dumps({"data": list(self.threebot.packages.list_all())})
    +
    +    @actor_method
    +    def add_package(self, path: str = "", giturl: str = "", extras=None) -> str:
    +        extras = extras or {}
    +        if path:
    +            path = path.strip()
    +        if giturl:
    +            giturl = giturl.strip()
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.add(path=path, giturl=giturl, **extras)})
    +
    +    @actor_method
    +    def add_internal_package(self, name, extras=None) -> str:
    +        """
    +        add internal package with name
    +
    +        this will search `j.packages` namespace for the given name.
    +
    +        Args:
    +            name (str): name of the package, e.g. codeserver.
    +            extras (dict, optional): extras to be passed to `package.install`. Defaults to None.
    +
    +        Raises:
    +            j.exceptions.NotFound: in case package cannot be found under `j.packages`
    +
    +        Returns:
    +            str: package information as json
    +        """
    +        try:
    +            package_module = getattr(j.packages, name)
    +        except AttributeError:
    +            raise j.exceptions.NotFound(f'package with name "{name}" cannot be found')
    +
    +        path = package_module.__path__[0]
    +        return self.add_package(path=path, extras=extras)
    +
    +    @actor_method
    +    def delete_package(self, name: str) -> str:
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.delete(name)})
    +
    +    @actor_method
    +    def list_chat_urls(self, name: str) -> str:
    +        package_chats = []
    +        if name in self.threebot.packages.packages:
    +            package = self.threebot.packages.get(name)
    +            if name in self.threebot.chatbot.chats:
    +                package_chat_names = self.threebot.chatbot.chats[name].keys()
    +                for chat_name in package_chat_names:
    +                    package_chats.append({"name": chat_name, "url": f"{package.base_url}/chats/{chat_name}"})
    +        return j.data.serializers.json.dumps({"data": package_chats})
    +
    +
    +Actor = Packages
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Packages +
    +
    +
    +
    + +Expand source code + +
    class Packages(BaseActor):
    +    def __init__(self):
    +        self._threebot = None
    +
    +    @property
    +    def threebot(self):
    +        if not self._threebot:
    +            self._threebot = j.servers.threebot.get()
    +        return self._threebot
    +
    +    @actor_method
    +    def get_package_status(self, names: list) -> str:
    +        return "hello from packages_get_status actor"
    +
    +    @actor_method
    +    def list_packages(self) -> str:
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.get_packages()})
    +
    +    @actor_method
    +    def packages_names(self) -> str:
    +        return j.data.serializers.json.dumps({"data": list(self.threebot.packages.list_all())})
    +
    +    @actor_method
    +    def add_package(self, path: str = "", giturl: str = "", extras=None) -> str:
    +        extras = extras or {}
    +        if path:
    +            path = path.strip()
    +        if giturl:
    +            giturl = giturl.strip()
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.add(path=path, giturl=giturl, **extras)})
    +
    +    @actor_method
    +    def add_internal_package(self, name, extras=None) -> str:
    +        """
    +        add internal package with name
    +
    +        this will search `j.packages` namespace for the given name.
    +
    +        Args:
    +            name (str): name of the package, e.g. codeserver.
    +            extras (dict, optional): extras to be passed to `package.install`. Defaults to None.
    +
    +        Raises:
    +            j.exceptions.NotFound: in case package cannot be found under `j.packages`
    +
    +        Returns:
    +            str: package information as json
    +        """
    +        try:
    +            package_module = getattr(j.packages, name)
    +        except AttributeError:
    +            raise j.exceptions.NotFound(f'package with name "{name}" cannot be found')
    +
    +        path = package_module.__path__[0]
    +        return self.add_package(path=path, extras=extras)
    +
    +    @actor_method
    +    def delete_package(self, name: str) -> str:
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.delete(name)})
    +
    +    @actor_method
    +    def list_chat_urls(self, name: str) -> str:
    +        package_chats = []
    +        if name in self.threebot.packages.packages:
    +            package = self.threebot.packages.get(name)
    +            if name in self.threebot.chatbot.chats:
    +                package_chat_names = self.threebot.chatbot.chats[name].keys()
    +                for chat_name in package_chat_names:
    +                    package_chats.append({"name": chat_name, "url": f"{package.base_url}/chats/{chat_name}"})
    +        return j.data.serializers.json.dumps({"data": package_chats})
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var threebot
    +
    +
    +
    + +Expand source code + +
    @property
    +def threebot(self):
    +    if not self._threebot:
    +        self._threebot = j.servers.threebot.get()
    +    return self._threebot
    +
    +
    +
    +

    Methods

    +
    +
    +def add_internal_package(self, name, extras=None) ‑> str +
    +
    +

    add internal package with name

    +

    this will search j.packages namespace for the given name.

    +

    Args

    +
    +
    name : str
    +
    name of the package, e.g. codeserver.
    +
    extras : dict, optional
    +
    extras to be passed to package.install. Defaults to None.
    +
    +

    Raises

    +
    +
    j.exceptions.NotFound
    +
    in case package cannot be found under j.packages
    +
    +

    Returns

    +
    +
    str
    +
    package information as json
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_internal_package(self, name, extras=None) -> str:
    +    """
    +    add internal package with name
    +
    +    this will search `j.packages` namespace for the given name.
    +
    +    Args:
    +        name (str): name of the package, e.g. codeserver.
    +        extras (dict, optional): extras to be passed to `package.install`. Defaults to None.
    +
    +    Raises:
    +        j.exceptions.NotFound: in case package cannot be found under `j.packages`
    +
    +    Returns:
    +        str: package information as json
    +    """
    +    try:
    +        package_module = getattr(j.packages, name)
    +    except AttributeError:
    +        raise j.exceptions.NotFound(f'package with name "{name}" cannot be found')
    +
    +    path = package_module.__path__[0]
    +    return self.add_package(path=path, extras=extras)
    +
    +
    +
    +def add_package(self, path: str = '', giturl: str = '', extras=None) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_package(self, path: str = "", giturl: str = "", extras=None) -> str:
    +    extras = extras or {}
    +    if path:
    +        path = path.strip()
    +    if giturl:
    +        giturl = giturl.strip()
    +    return j.data.serializers.json.dumps({"data": self.threebot.packages.add(path=path, giturl=giturl, **extras)})
    +
    +
    +
    +def delete_package(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_package(self, name: str) -> str:
    +    return j.data.serializers.json.dumps({"data": self.threebot.packages.delete(name)})
    +
    +
    +
    +def get_package_status(self, names: list) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_package_status(self, names: list) -> str:
    +    return "hello from packages_get_status actor"
    +
    +
    +
    +def list_chat_urls(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_chat_urls(self, name: str) -> str:
    +    package_chats = []
    +    if name in self.threebot.packages.packages:
    +        package = self.threebot.packages.get(name)
    +        if name in self.threebot.chatbot.chats:
    +            package_chat_names = self.threebot.chatbot.chats[name].keys()
    +            for chat_name in package_chat_names:
    +                package_chats.append({"name": chat_name, "url": f"{package.base_url}/chats/{chat_name}"})
    +    return j.data.serializers.json.dumps({"data": package_chats})
    +
    +
    +
    +def list_packages(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_packages(self) -> str:
    +    return j.data.serializers.json.dumps({"data": self.threebot.packages.get_packages()})
    +
    +
    +
    +def packages_names(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def packages_names(self) -> str:
    +    return j.data.serializers.json.dumps({"data": list(self.threebot.packages.list_all())})
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class Packages(BaseActor):
    +    def __init__(self):
    +        self._threebot = None
    +
    +    @property
    +    def threebot(self):
    +        if not self._threebot:
    +            self._threebot = j.servers.threebot.get()
    +        return self._threebot
    +
    +    @actor_method
    +    def get_package_status(self, names: list) -> str:
    +        return "hello from packages_get_status actor"
    +
    +    @actor_method
    +    def list_packages(self) -> str:
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.get_packages()})
    +
    +    @actor_method
    +    def packages_names(self) -> str:
    +        return j.data.serializers.json.dumps({"data": list(self.threebot.packages.list_all())})
    +
    +    @actor_method
    +    def add_package(self, path: str = "", giturl: str = "", extras=None) -> str:
    +        extras = extras or {}
    +        if path:
    +            path = path.strip()
    +        if giturl:
    +            giturl = giturl.strip()
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.add(path=path, giturl=giturl, **extras)})
    +
    +    @actor_method
    +    def add_internal_package(self, name, extras=None) -> str:
    +        """
    +        add internal package with name
    +
    +        this will search `j.packages` namespace for the given name.
    +
    +        Args:
    +            name (str): name of the package, e.g. codeserver.
    +            extras (dict, optional): extras to be passed to `package.install`. Defaults to None.
    +
    +        Raises:
    +            j.exceptions.NotFound: in case package cannot be found under `j.packages`
    +
    +        Returns:
    +            str: package information as json
    +        """
    +        try:
    +            package_module = getattr(j.packages, name)
    +        except AttributeError:
    +            raise j.exceptions.NotFound(f'package with name "{name}" cannot be found')
    +
    +        path = package_module.__path__[0]
    +        return self.add_package(path=path, extras=extras)
    +
    +    @actor_method
    +    def delete_package(self, name: str) -> str:
    +        return j.data.serializers.json.dumps({"data": self.threebot.packages.delete(name)})
    +
    +    @actor_method
    +    def list_chat_urls(self, name: str) -> str:
    +        package_chats = []
    +        if name in self.threebot.packages.packages:
    +            package = self.threebot.packages.get(name)
    +            if name in self.threebot.chatbot.chats:
    +                package_chat_names = self.threebot.chatbot.chats[name].keys()
    +                for chat_name in package_chat_names:
    +                    package_chats.append({"name": chat_name, "url": f"{package.base_url}/chats/{chat_name}"})
    +        return j.data.serializers.json.dumps({"data": package_chats})
    +
    +

    Ancestors

    + +

    Instance variables

    +
    +
    var threebot
    +
    +
    +
    + +Expand source code + +
    @property
    +def threebot(self):
    +    if not self._threebot:
    +        self._threebot = j.servers.threebot.get()
    +    return self._threebot
    +
    +
    +
    +

    Methods

    +
    +
    +def add_internal_package(self, name, extras=None) ‑> str +
    +
    +

    add internal package with name

    +

    this will search j.packages namespace for the given name.

    +

    Args

    +
    +
    name : str
    +
    name of the package, e.g. codeserver.
    +
    extras : dict, optional
    +
    extras to be passed to package.install. Defaults to None.
    +
    +

    Raises

    +
    +
    j.exceptions.NotFound
    +
    in case package cannot be found under j.packages
    +
    +

    Returns

    +
    +
    str
    +
    package information as json
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_internal_package(self, name, extras=None) -> str:
    +    """
    +    add internal package with name
    +
    +    this will search `j.packages` namespace for the given name.
    +
    +    Args:
    +        name (str): name of the package, e.g. codeserver.
    +        extras (dict, optional): extras to be passed to `package.install`. Defaults to None.
    +
    +    Raises:
    +        j.exceptions.NotFound: in case package cannot be found under `j.packages`
    +
    +    Returns:
    +        str: package information as json
    +    """
    +    try:
    +        package_module = getattr(j.packages, name)
    +    except AttributeError:
    +        raise j.exceptions.NotFound(f'package with name "{name}" cannot be found')
    +
    +    path = package_module.__path__[0]
    +    return self.add_package(path=path, extras=extras)
    +
    +
    +
    +def add_package(self, path: str = '', giturl: str = '', extras=None) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def add_package(self, path: str = "", giturl: str = "", extras=None) -> str:
    +    extras = extras or {}
    +    if path:
    +        path = path.strip()
    +    if giturl:
    +        giturl = giturl.strip()
    +    return j.data.serializers.json.dumps({"data": self.threebot.packages.add(path=path, giturl=giturl, **extras)})
    +
    +
    +
    +def delete_package(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_package(self, name: str) -> str:
    +    return j.data.serializers.json.dumps({"data": self.threebot.packages.delete(name)})
    +
    +
    +
    +def get_package_status(self, names: list) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_package_status(self, names: list) -> str:
    +    return "hello from packages_get_status actor"
    +
    +
    +
    +def list_chat_urls(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_chat_urls(self, name: str) -> str:
    +    package_chats = []
    +    if name in self.threebot.packages.packages:
    +        package = self.threebot.packages.get(name)
    +        if name in self.threebot.chatbot.chats:
    +            package_chat_names = self.threebot.chatbot.chats[name].keys()
    +            for chat_name in package_chat_names:
    +                package_chats.append({"name": chat_name, "url": f"{package.base_url}/chats/{chat_name}"})
    +    return j.data.serializers.json.dumps({"data": package_chats})
    +
    +
    +
    +def list_packages(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def list_packages(self) -> str:
    +    return j.data.serializers.json.dumps({"data": self.threebot.packages.get_packages()})
    +
    +
    +
    +def packages_names(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def packages_names(self) -> str:
    +    return j.data.serializers.json.dumps({"data": list(self.threebot.packages.list_all())})
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/actors/wallet.html b/docs/api/jumpscale/packages/admin/actors/wallet.html new file mode 100644 index 000000000..b95d9723a --- /dev/null +++ b/docs/api/jumpscale/packages/admin/actors/wallet.html @@ -0,0 +1,781 @@ + + + + + + +jumpscale.packages.admin.actors.wallet API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.actors.wallet

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +from jumpscale.clients.stellar.stellar import _NETWORK_KNOWN_TRUSTS
    +
    +
    +class Wallet(BaseActor):
    +    @actor_method
    +    def create_wallet(self, name: str) -> str:
    +        if j.clients.stellar.find(name):
    +            raise j.exceptions.Value(f"Wallet {name} already exists")
    +
    +        wallet = j.clients.stellar.new(name=name)
    +        try:
    +            wallet.activate_through_threefold_service()
    +        except Exception:
    +            j.clients.stellar.delete(name=name)
    +            raise j.exceptions.JSException("Error on wallet activation")
    +
    +        try:
    +            wallet.add_known_trustline("TFT")
    +        except Exception:
    +            j.clients.stellar.delete(name=name)
    +            raise j.exceptions.JSException(
    +                f"Failed to add trustlines to wallet {name}. Any changes made will be reverted."
    +            )
    +
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +    @actor_method
    +    def get_wallet_info(self, name: str) -> str:
    +        if not j.clients.stellar.find(name):
    +            raise j.exceptions.Value("Wallet does not exist")
    +
    +        wallet = j.clients.stellar.get(name=name)
    +        error = ""
    +        ret = {}
    +        try:
    +            balances = wallet.get_balance()
    +            balances_data = []
    +            for item in balances.balances:
    +                balances_data.append(
    +                    {"balance": item.balance, "asset_code": item.asset_code, "asset_issuer": item.asset_issuer}
    +                )
    +
    +            qrcode_amount = 100  # user can modify it after scanning QR code
    +            qrcode_data = f"TFT:{wallet.address}?amount={qrcode_amount}&message=topup&sender=me"
    +            qrcode_image = j.tools.qrcode.base64_get(qrcode_data, scale=2)
    +
    +            ret = {
    +                "address": wallet.address,
    +                "network": wallet.network.value,
    +                "secret": wallet.secret,
    +                "balances": balances_data,
    +                "qrcode": qrcode_image,
    +            }
    +        except Exception as e:
    +            error = str(e)
    +            j.logger.error(error)
    +
    +        return j.data.serializers.json.dumps({"data": ret, "error": error})
    +
    +    @actor_method
    +    def get_wallets(self) -> str:
    +        wallets = j.clients.stellar.list_all()
    +        ret = []
    +        for name in wallets:
    +            wallet = j.clients.stellar.get(name=name)
    +            ret.append({"name": wallet.instance_name, "address": wallet.address, "network": wallet.network.name})
    +
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def update_trustlines(self, name: str) -> str:
    +        if not j.clients.stellar.find(name):
    +            raise j.exceptions.Value("Wallet does not exist")
    +
    +        wallet = j.clients.stellar.get(name=name)
    +        trustlines = _NETWORK_KNOWN_TRUSTS[str(wallet.network.name)].copy()
    +        for balance in wallet.get_balance().balances:
    +            if balance.asset_code in trustlines:
    +                trustlines.pop(balance.asset_code)
    +        if "TFT" in trustlines.keys():
    +            wallet.add_known_trustline("TFT")
    +
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": trustlines})
    +
    +    @actor_method
    +    def import_wallet(self, name: str, secret: str) -> str:
    +        if name in j.clients.stellar.list_all():
    +            return j.data.serializers.json.dumps({"error": "Wallet name already exists"})
    +        try:
    +            wallet = j.clients.stellar.new(name=name, secret=secret)
    +        except Exception as e:
    +            j.clients.stellar.delete(name)
    +            return j.data.serializers.json.dumps({"error": str(e)})
    +        try:
    +            wallet.get_balance()
    +        except:
    +            j.clients.stellar.delete(name)
    +            return j.data.serializers.json.dumps(
    +                {"error": "Import failed. Make sure wallet is activated on STD network."}
    +            )
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +    @actor_method
    +    def delete_wallet(self, name: str) -> str:
    +        j.clients.stellar.delete(name=name)
    +        return j.data.serializers.json.dumps({"data": True})
    +
    +
    +Actor = Wallet
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Wallet +
    +
    +
    +
    + +Expand source code + +
    class Wallet(BaseActor):
    +    @actor_method
    +    def create_wallet(self, name: str) -> str:
    +        if j.clients.stellar.find(name):
    +            raise j.exceptions.Value(f"Wallet {name} already exists")
    +
    +        wallet = j.clients.stellar.new(name=name)
    +        try:
    +            wallet.activate_through_threefold_service()
    +        except Exception:
    +            j.clients.stellar.delete(name=name)
    +            raise j.exceptions.JSException("Error on wallet activation")
    +
    +        try:
    +            wallet.add_known_trustline("TFT")
    +        except Exception:
    +            j.clients.stellar.delete(name=name)
    +            raise j.exceptions.JSException(
    +                f"Failed to add trustlines to wallet {name}. Any changes made will be reverted."
    +            )
    +
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +    @actor_method
    +    def get_wallet_info(self, name: str) -> str:
    +        if not j.clients.stellar.find(name):
    +            raise j.exceptions.Value("Wallet does not exist")
    +
    +        wallet = j.clients.stellar.get(name=name)
    +        error = ""
    +        ret = {}
    +        try:
    +            balances = wallet.get_balance()
    +            balances_data = []
    +            for item in balances.balances:
    +                balances_data.append(
    +                    {"balance": item.balance, "asset_code": item.asset_code, "asset_issuer": item.asset_issuer}
    +                )
    +
    +            qrcode_amount = 100  # user can modify it after scanning QR code
    +            qrcode_data = f"TFT:{wallet.address}?amount={qrcode_amount}&message=topup&sender=me"
    +            qrcode_image = j.tools.qrcode.base64_get(qrcode_data, scale=2)
    +
    +            ret = {
    +                "address": wallet.address,
    +                "network": wallet.network.value,
    +                "secret": wallet.secret,
    +                "balances": balances_data,
    +                "qrcode": qrcode_image,
    +            }
    +        except Exception as e:
    +            error = str(e)
    +            j.logger.error(error)
    +
    +        return j.data.serializers.json.dumps({"data": ret, "error": error})
    +
    +    @actor_method
    +    def get_wallets(self) -> str:
    +        wallets = j.clients.stellar.list_all()
    +        ret = []
    +        for name in wallets:
    +            wallet = j.clients.stellar.get(name=name)
    +            ret.append({"name": wallet.instance_name, "address": wallet.address, "network": wallet.network.name})
    +
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def update_trustlines(self, name: str) -> str:
    +        if not j.clients.stellar.find(name):
    +            raise j.exceptions.Value("Wallet does not exist")
    +
    +        wallet = j.clients.stellar.get(name=name)
    +        trustlines = _NETWORK_KNOWN_TRUSTS[str(wallet.network.name)].copy()
    +        for balance in wallet.get_balance().balances:
    +            if balance.asset_code in trustlines:
    +                trustlines.pop(balance.asset_code)
    +        if "TFT" in trustlines.keys():
    +            wallet.add_known_trustline("TFT")
    +
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": trustlines})
    +
    +    @actor_method
    +    def import_wallet(self, name: str, secret: str) -> str:
    +        if name in j.clients.stellar.list_all():
    +            return j.data.serializers.json.dumps({"error": "Wallet name already exists"})
    +        try:
    +            wallet = j.clients.stellar.new(name=name, secret=secret)
    +        except Exception as e:
    +            j.clients.stellar.delete(name)
    +            return j.data.serializers.json.dumps({"error": str(e)})
    +        try:
    +            wallet.get_balance()
    +        except:
    +            j.clients.stellar.delete(name)
    +            return j.data.serializers.json.dumps(
    +                {"error": "Import failed. Make sure wallet is activated on STD network."}
    +            )
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +    @actor_method
    +    def delete_wallet(self, name: str) -> str:
    +        j.clients.stellar.delete(name=name)
    +        return j.data.serializers.json.dumps({"data": True})
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def create_wallet(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def create_wallet(self, name: str) -> str:
    +    if j.clients.stellar.find(name):
    +        raise j.exceptions.Value(f"Wallet {name} already exists")
    +
    +    wallet = j.clients.stellar.new(name=name)
    +    try:
    +        wallet.activate_through_threefold_service()
    +    except Exception:
    +        j.clients.stellar.delete(name=name)
    +        raise j.exceptions.JSException("Error on wallet activation")
    +
    +    try:
    +        wallet.add_known_trustline("TFT")
    +    except Exception:
    +        j.clients.stellar.delete(name=name)
    +        raise j.exceptions.JSException(
    +            f"Failed to add trustlines to wallet {name}. Any changes made will be reverted."
    +        )
    +
    +    wallet.save()
    +    return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +
    +
    +def delete_wallet(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_wallet(self, name: str) -> str:
    +    j.clients.stellar.delete(name=name)
    +    return j.data.serializers.json.dumps({"data": True})
    +
    +
    +
    +def get_wallet_info(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_wallet_info(self, name: str) -> str:
    +    if not j.clients.stellar.find(name):
    +        raise j.exceptions.Value("Wallet does not exist")
    +
    +    wallet = j.clients.stellar.get(name=name)
    +    error = ""
    +    ret = {}
    +    try:
    +        balances = wallet.get_balance()
    +        balances_data = []
    +        for item in balances.balances:
    +            balances_data.append(
    +                {"balance": item.balance, "asset_code": item.asset_code, "asset_issuer": item.asset_issuer}
    +            )
    +
    +        qrcode_amount = 100  # user can modify it after scanning QR code
    +        qrcode_data = f"TFT:{wallet.address}?amount={qrcode_amount}&message=topup&sender=me"
    +        qrcode_image = j.tools.qrcode.base64_get(qrcode_data, scale=2)
    +
    +        ret = {
    +            "address": wallet.address,
    +            "network": wallet.network.value,
    +            "secret": wallet.secret,
    +            "balances": balances_data,
    +            "qrcode": qrcode_image,
    +        }
    +    except Exception as e:
    +        error = str(e)
    +        j.logger.error(error)
    +
    +    return j.data.serializers.json.dumps({"data": ret, "error": error})
    +
    +
    +
    +def get_wallets(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_wallets(self) -> str:
    +    wallets = j.clients.stellar.list_all()
    +    ret = []
    +    for name in wallets:
    +        wallet = j.clients.stellar.get(name=name)
    +        ret.append({"name": wallet.instance_name, "address": wallet.address, "network": wallet.network.name})
    +
    +    return j.data.serializers.json.dumps({"data": ret})
    +
    +
    +
    +def import_wallet(self, name: str, secret: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def import_wallet(self, name: str, secret: str) -> str:
    +    if name in j.clients.stellar.list_all():
    +        return j.data.serializers.json.dumps({"error": "Wallet name already exists"})
    +    try:
    +        wallet = j.clients.stellar.new(name=name, secret=secret)
    +    except Exception as e:
    +        j.clients.stellar.delete(name)
    +        return j.data.serializers.json.dumps({"error": str(e)})
    +    try:
    +        wallet.get_balance()
    +    except:
    +        j.clients.stellar.delete(name)
    +        return j.data.serializers.json.dumps(
    +            {"error": "Import failed. Make sure wallet is activated on STD network."}
    +        )
    +    wallet.save()
    +    return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +
    +
    +def update_trustlines(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def update_trustlines(self, name: str) -> str:
    +    if not j.clients.stellar.find(name):
    +        raise j.exceptions.Value("Wallet does not exist")
    +
    +    wallet = j.clients.stellar.get(name=name)
    +    trustlines = _NETWORK_KNOWN_TRUSTS[str(wallet.network.name)].copy()
    +    for balance in wallet.get_balance().balances:
    +        if balance.asset_code in trustlines:
    +            trustlines.pop(balance.asset_code)
    +    if "TFT" in trustlines.keys():
    +        wallet.add_known_trustline("TFT")
    +
    +    wallet.save()
    +    return j.data.serializers.json.dumps({"data": trustlines})
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class Wallet(BaseActor):
    +    @actor_method
    +    def create_wallet(self, name: str) -> str:
    +        if j.clients.stellar.find(name):
    +            raise j.exceptions.Value(f"Wallet {name} already exists")
    +
    +        wallet = j.clients.stellar.new(name=name)
    +        try:
    +            wallet.activate_through_threefold_service()
    +        except Exception:
    +            j.clients.stellar.delete(name=name)
    +            raise j.exceptions.JSException("Error on wallet activation")
    +
    +        try:
    +            wallet.add_known_trustline("TFT")
    +        except Exception:
    +            j.clients.stellar.delete(name=name)
    +            raise j.exceptions.JSException(
    +                f"Failed to add trustlines to wallet {name}. Any changes made will be reverted."
    +            )
    +
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +    @actor_method
    +    def get_wallet_info(self, name: str) -> str:
    +        if not j.clients.stellar.find(name):
    +            raise j.exceptions.Value("Wallet does not exist")
    +
    +        wallet = j.clients.stellar.get(name=name)
    +        error = ""
    +        ret = {}
    +        try:
    +            balances = wallet.get_balance()
    +            balances_data = []
    +            for item in balances.balances:
    +                balances_data.append(
    +                    {"balance": item.balance, "asset_code": item.asset_code, "asset_issuer": item.asset_issuer}
    +                )
    +
    +            qrcode_amount = 100  # user can modify it after scanning QR code
    +            qrcode_data = f"TFT:{wallet.address}?amount={qrcode_amount}&message=topup&sender=me"
    +            qrcode_image = j.tools.qrcode.base64_get(qrcode_data, scale=2)
    +
    +            ret = {
    +                "address": wallet.address,
    +                "network": wallet.network.value,
    +                "secret": wallet.secret,
    +                "balances": balances_data,
    +                "qrcode": qrcode_image,
    +            }
    +        except Exception as e:
    +            error = str(e)
    +            j.logger.error(error)
    +
    +        return j.data.serializers.json.dumps({"data": ret, "error": error})
    +
    +    @actor_method
    +    def get_wallets(self) -> str:
    +        wallets = j.clients.stellar.list_all()
    +        ret = []
    +        for name in wallets:
    +            wallet = j.clients.stellar.get(name=name)
    +            ret.append({"name": wallet.instance_name, "address": wallet.address, "network": wallet.network.name})
    +
    +        return j.data.serializers.json.dumps({"data": ret})
    +
    +    @actor_method
    +    def update_trustlines(self, name: str) -> str:
    +        if not j.clients.stellar.find(name):
    +            raise j.exceptions.Value("Wallet does not exist")
    +
    +        wallet = j.clients.stellar.get(name=name)
    +        trustlines = _NETWORK_KNOWN_TRUSTS[str(wallet.network.name)].copy()
    +        for balance in wallet.get_balance().balances:
    +            if balance.asset_code in trustlines:
    +                trustlines.pop(balance.asset_code)
    +        if "TFT" in trustlines.keys():
    +            wallet.add_known_trustline("TFT")
    +
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": trustlines})
    +
    +    @actor_method
    +    def import_wallet(self, name: str, secret: str) -> str:
    +        if name in j.clients.stellar.list_all():
    +            return j.data.serializers.json.dumps({"error": "Wallet name already exists"})
    +        try:
    +            wallet = j.clients.stellar.new(name=name, secret=secret)
    +        except Exception as e:
    +            j.clients.stellar.delete(name)
    +            return j.data.serializers.json.dumps({"error": str(e)})
    +        try:
    +            wallet.get_balance()
    +        except:
    +            j.clients.stellar.delete(name)
    +            return j.data.serializers.json.dumps(
    +                {"error": "Import failed. Make sure wallet is activated on STD network."}
    +            )
    +        wallet.save()
    +        return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +    @actor_method
    +    def delete_wallet(self, name: str) -> str:
    +        j.clients.stellar.delete(name=name)
    +        return j.data.serializers.json.dumps({"data": True})
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def create_wallet(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def create_wallet(self, name: str) -> str:
    +    if j.clients.stellar.find(name):
    +        raise j.exceptions.Value(f"Wallet {name} already exists")
    +
    +    wallet = j.clients.stellar.new(name=name)
    +    try:
    +        wallet.activate_through_threefold_service()
    +    except Exception:
    +        j.clients.stellar.delete(name=name)
    +        raise j.exceptions.JSException("Error on wallet activation")
    +
    +    try:
    +        wallet.add_known_trustline("TFT")
    +    except Exception:
    +        j.clients.stellar.delete(name=name)
    +        raise j.exceptions.JSException(
    +            f"Failed to add trustlines to wallet {name}. Any changes made will be reverted."
    +        )
    +
    +    wallet.save()
    +    return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +
    +
    +def delete_wallet(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def delete_wallet(self, name: str) -> str:
    +    j.clients.stellar.delete(name=name)
    +    return j.data.serializers.json.dumps({"data": True})
    +
    +
    +
    +def get_wallet_info(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_wallet_info(self, name: str) -> str:
    +    if not j.clients.stellar.find(name):
    +        raise j.exceptions.Value("Wallet does not exist")
    +
    +    wallet = j.clients.stellar.get(name=name)
    +    error = ""
    +    ret = {}
    +    try:
    +        balances = wallet.get_balance()
    +        balances_data = []
    +        for item in balances.balances:
    +            balances_data.append(
    +                {"balance": item.balance, "asset_code": item.asset_code, "asset_issuer": item.asset_issuer}
    +            )
    +
    +        qrcode_amount = 100  # user can modify it after scanning QR code
    +        qrcode_data = f"TFT:{wallet.address}?amount={qrcode_amount}&message=topup&sender=me"
    +        qrcode_image = j.tools.qrcode.base64_get(qrcode_data, scale=2)
    +
    +        ret = {
    +            "address": wallet.address,
    +            "network": wallet.network.value,
    +            "secret": wallet.secret,
    +            "balances": balances_data,
    +            "qrcode": qrcode_image,
    +        }
    +    except Exception as e:
    +        error = str(e)
    +        j.logger.error(error)
    +
    +    return j.data.serializers.json.dumps({"data": ret, "error": error})
    +
    +
    +
    +def get_wallets(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def get_wallets(self) -> str:
    +    wallets = j.clients.stellar.list_all()
    +    ret = []
    +    for name in wallets:
    +        wallet = j.clients.stellar.get(name=name)
    +        ret.append({"name": wallet.instance_name, "address": wallet.address, "network": wallet.network.name})
    +
    +    return j.data.serializers.json.dumps({"data": ret})
    +
    +
    +
    +def import_wallet(self, name: str, secret: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def import_wallet(self, name: str, secret: str) -> str:
    +    if name in j.clients.stellar.list_all():
    +        return j.data.serializers.json.dumps({"error": "Wallet name already exists"})
    +    try:
    +        wallet = j.clients.stellar.new(name=name, secret=secret)
    +    except Exception as e:
    +        j.clients.stellar.delete(name)
    +        return j.data.serializers.json.dumps({"error": str(e)})
    +    try:
    +        wallet.get_balance()
    +    except:
    +        j.clients.stellar.delete(name)
    +        return j.data.serializers.json.dumps(
    +            {"error": "Import failed. Make sure wallet is activated on STD network."}
    +        )
    +    wallet.save()
    +    return j.data.serializers.json.dumps({"data": wallet.address})
    +
    +
    +
    +def update_trustlines(self, name: str) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def update_trustlines(self, name: str) -> str:
    +    if not j.clients.stellar.find(name):
    +        raise j.exceptions.Value("Wallet does not exist")
    +
    +    wallet = j.clients.stellar.get(name=name)
    +    trustlines = _NETWORK_KNOWN_TRUSTS[str(wallet.network.name)].copy()
    +    for balance in wallet.get_balance().balances:
    +        if balance.asset_code in trustlines:
    +            trustlines.pop(balance.asset_code)
    +    if "TFT" in trustlines.keys():
    +        wallet.add_known_trustline("TFT")
    +
    +    wallet.save()
    +    return j.data.serializers.json.dumps({"data": trustlines})
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/bottle/admin.html b/docs/api/jumpscale/packages/admin/bottle/admin.html new file mode 100644 index 000000000..f47ac897f --- /dev/null +++ b/docs/api/jumpscale/packages/admin/bottle/admin.html @@ -0,0 +1,279 @@ + + + + + + +jumpscale.packages.admin.bottle.admin API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.bottle.admin

    +
    +
    +
    + +Expand source code + +
    from bottle import Bottle, request, HTTPResponse, static_file
    +
    +from jumpscale.loader import j
    +from jumpscale.packages.auth.bottle.auth import login_required, get_user_info
    +from jumpscale.packages.admin.bottle.models import UserEntry
    +from jumpscale.core.base import StoredFactory
    +
    +app = Bottle()
    +
    +
    +@app.route("/api/allowed", method="GET")
    +@login_required
    +def allowed():
    +    user_factory = StoredFactory(UserEntry)
    +    user_info = j.data.serializers.json.loads(get_user_info())
    +    tname = user_info["username"]
    +    instances = user_factory.list_all()
    +    for name in instances:
    +        user_entry = user_factory.get(name)
    +        if user_entry.tname == tname and user_entry.has_agreed:
    +            return j.data.serializers.json.dumps({"allowed": True})
    +    return j.data.serializers.json.dumps({"allowed": False})
    +
    +
    +@app.route("/api/accept", method="GET")
    +@login_required
    +def accept():
    +    user_factory = StoredFactory(UserEntry)
    +
    +    user_info = j.data.serializers.json.loads(get_user_info())
    +    tname = user_info["username"]
    +
    +    user_entry = user_factory.get(f"{tname.replace('.3bot', '')}")
    +    if user_entry.has_agreed:
    +        return HTTPResponse(
    +            j.data.serializers.json.dumps({"allowed": True}), status=200, headers={"Content-Type": "application/json"}
    +        )
    +    else:
    +        user_entry.has_agreed = True
    +        user_entry.tname = tname
    +        user_entry.save()
    +        return HTTPResponse(
    +            j.data.serializers.json.dumps({"allowed": True}), status=201, headers={"Content-Type": "application/json"}
    +        )
    +
    +
    +@app.route("/api/announced", method="GET")
    +@login_required
    +def announced():
    +    result = bool(j.config.get("ANNOUNCED"))
    +
    +    return HTTPResponse(
    +        j.data.serializers.json.dumps({"announced": result}), status=200, headers={"Content-Type": "application/json"}
    +    )
    +
    +
    +@app.route("/api/announce", method="GET")
    +@login_required
    +def announce():
    +    j.config.set("ANNOUNCED", True)
    +    return HTTPResponse(
    +        j.data.serializers.json.dumps({"announced": True}), status=200, headers={"Content-Type": "application/json"}
    +    )
    +
    +
    +@app.route("/api/heartbeat", method="GET")
    +def heartbeat():
    +    return HTTPResponse(status=200)
    +
    +
    +@app.route("/api/export")
    +@login_required
    +def export():
    +    filename = j.tools.export.export_threebot_state()
    +    exporttime = j.data.time.now().format("YYYY-MM-DDTHH-mm-ssZZ")
    +    return static_file(
    +        j.sals.fs.basename(filename),
    +        root=j.sals.fs.dirname(filename),
    +        download=f"export-{exporttime}.tar.gz",
    +        mimetype="application/gzip",
    +    )
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def accept() +
    +
    +
    +
    + +Expand source code + +
    @app.route("/api/accept", method="GET")
    +@login_required
    +def accept():
    +    user_factory = StoredFactory(UserEntry)
    +
    +    user_info = j.data.serializers.json.loads(get_user_info())
    +    tname = user_info["username"]
    +
    +    user_entry = user_factory.get(f"{tname.replace('.3bot', '')}")
    +    if user_entry.has_agreed:
    +        return HTTPResponse(
    +            j.data.serializers.json.dumps({"allowed": True}), status=200, headers={"Content-Type": "application/json"}
    +        )
    +    else:
    +        user_entry.has_agreed = True
    +        user_entry.tname = tname
    +        user_entry.save()
    +        return HTTPResponse(
    +            j.data.serializers.json.dumps({"allowed": True}), status=201, headers={"Content-Type": "application/json"}
    +        )
    +
    +
    +
    +def allowed() +
    +
    +
    +
    + +Expand source code + +
    @app.route("/api/allowed", method="GET")
    +@login_required
    +def allowed():
    +    user_factory = StoredFactory(UserEntry)
    +    user_info = j.data.serializers.json.loads(get_user_info())
    +    tname = user_info["username"]
    +    instances = user_factory.list_all()
    +    for name in instances:
    +        user_entry = user_factory.get(name)
    +        if user_entry.tname == tname and user_entry.has_agreed:
    +            return j.data.serializers.json.dumps({"allowed": True})
    +    return j.data.serializers.json.dumps({"allowed": False})
    +
    +
    +
    +def announce() +
    +
    +
    +
    + +Expand source code + +
    @app.route("/api/announce", method="GET")
    +@login_required
    +def announce():
    +    j.config.set("ANNOUNCED", True)
    +    return HTTPResponse(
    +        j.data.serializers.json.dumps({"announced": True}), status=200, headers={"Content-Type": "application/json"}
    +    )
    +
    +
    +
    +def announced() +
    +
    +
    +
    + +Expand source code + +
    @app.route("/api/announced", method="GET")
    +@login_required
    +def announced():
    +    result = bool(j.config.get("ANNOUNCED"))
    +
    +    return HTTPResponse(
    +        j.data.serializers.json.dumps({"announced": result}), status=200, headers={"Content-Type": "application/json"}
    +    )
    +
    +
    +
    +def export() +
    +
    +
    +
    + +Expand source code + +
    @app.route("/api/export")
    +@login_required
    +def export():
    +    filename = j.tools.export.export_threebot_state()
    +    exporttime = j.data.time.now().format("YYYY-MM-DDTHH-mm-ssZZ")
    +    return static_file(
    +        j.sals.fs.basename(filename),
    +        root=j.sals.fs.dirname(filename),
    +        download=f"export-{exporttime}.tar.gz",
    +        mimetype="application/gzip",
    +    )
    +
    +
    +
    +def heartbeat() +
    +
    +
    +
    + +Expand source code + +
    @app.route("/api/heartbeat", method="GET")
    +def heartbeat():
    +    return HTTPResponse(status=200)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/bottle/index.html b/docs/api/jumpscale/packages/admin/bottle/index.html new file mode 100644 index 000000000..d44b826b1 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/bottle/index.html @@ -0,0 +1,70 @@ + + + + + + +jumpscale.packages.admin.bottle API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/bottle/models.html b/docs/api/jumpscale/packages/admin/bottle/models.html new file mode 100644 index 000000000..16b6882de --- /dev/null +++ b/docs/api/jumpscale/packages/admin/bottle/models.html @@ -0,0 +1,185 @@ + + + + + + +jumpscale.packages.admin.bottle.models API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.bottle.models

    +
    +
    +
    + +Expand source code + +
    from jumpscale.core.base import Base, fields
    +from jumpscale.loader import j
    +
    +
    +class UserEntry(Base):
    +    tname = fields.String()
    +    has_agreed = fields.Boolean(default=False)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class UserEntry +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class UserEntry(Base):
    +    tname = fields.String()
    +    has_agreed = fields.Boolean(default=False)
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var has_agreed
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var tname
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/index.html b/docs/api/jumpscale/packages/admin/index.html new file mode 100644 index 000000000..8a9c26c23 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/index.html @@ -0,0 +1,83 @@ + + + + + + +jumpscale.packages.admin API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/services/alerts_notifier.html b/docs/api/jumpscale/packages/admin/services/alerts_notifier.html new file mode 100644 index 000000000..c2ce9a1e3 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/services/alerts_notifier.html @@ -0,0 +1,153 @@ + + + + + + +jumpscale.packages.admin.services.alerts_notifier API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.services.alerts_notifier

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +
    +from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +
    +
    +class AlertsNotifier(BackgroundService):
    +    def __init__(self, interval=60 * 60, *args, **kwargs):
    +        """Notify the support [escalation_emails] with the hurly alerts count.
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def job(self):
    +        j.logger.info("Alerts support notifier service: service started")
    +        # Get the last hour alerts
    +        time_now = j.data.time.now()
    +        alerts = j.tools.alerthandler.find(end_time=time_now.timestamp - 60 * 60)
    +        # get the host info
    +        host_name = j.sals.nettools.get_host_name()
    +        ip_info = j.sals.nettools.get_default_ip_config()
    +        ip_address = ip_info[-1] if len(ip_info) else ""
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +
    +        if escalation_emails and alerts:
    +            mail_info = {
    +                "recipients_emails": escalation_emails,
    +                "sender": "monitor@threefold.com",
    +                "subject": f"ALerts report from {host_name}:{ip_address}",
    +                "message": f"""Last hour, {host_name}:{ip_address} raised {len(alerts)} alerts\nPlease check https://{ip_address}/admin \n{time_now.format('YYYY-MM-DD HH:mm:ss ZZ')}\n""",
    +            }
    +            j.core.db.rpush("MAIL_QUEUE", j.data.serializers.json.dumps(mail_info))
    +
    +
    +service = AlertsNotifier()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class AlertsNotifier +(interval=3600, *args, **kwargs) +
    +
    +

    Helper class that provides a standard way to create an ABC using +inheritance.

    +

    Notify the support [escalation_emails] with the hurly alerts count.

    +
    + +Expand source code + +
    class AlertsNotifier(BackgroundService):
    +    def __init__(self, interval=60 * 60, *args, **kwargs):
    +        """Notify the support [escalation_emails] with the hurly alerts count.
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def job(self):
    +        j.logger.info("Alerts support notifier service: service started")
    +        # Get the last hour alerts
    +        time_now = j.data.time.now()
    +        alerts = j.tools.alerthandler.find(end_time=time_now.timestamp - 60 * 60)
    +        # get the host info
    +        host_name = j.sals.nettools.get_host_name()
    +        ip_info = j.sals.nettools.get_default_ip_config()
    +        ip_address = ip_info[-1] if len(ip_info) else ""
    +        escalation_emails = j.core.config.get("ESCALATION_EMAILS", [])
    +
    +        if escalation_emails and alerts:
    +            mail_info = {
    +                "recipients_emails": escalation_emails,
    +                "sender": "monitor@threefold.com",
    +                "subject": f"ALerts report from {host_name}:{ip_address}",
    +                "message": f"""Last hour, {host_name}:{ip_address} raised {len(alerts)} alerts\nPlease check https://{ip_address}/admin \n{time_now.format('YYYY-MM-DD HH:mm:ss ZZ')}\n""",
    +            }
    +            j.core.db.rpush("MAIL_QUEUE", j.data.serializers.json.dumps(mail_info))
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/services/diskcheck.html b/docs/api/jumpscale/packages/admin/services/diskcheck.html new file mode 100644 index 000000000..f6a1ab697 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/services/diskcheck.html @@ -0,0 +1,133 @@ + + + + + + +jumpscale.packages.admin.services.diskcheck API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.services.diskcheck

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +from jumpscale.tools.notificationsqueue.queue import LEVEL
    +
    +
    +class DiskCheckService(BackgroundService):
    +    def __init__(self, interval="* * * * *", *args, **kwargs):
    +        """
    +            Check disk space every 1 minute
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def job(self):
    +        disk_obj = j.sals.fs.shutil.disk_usage("/")
    +        free_disk_space = disk_obj.free // (1024.0 ** 3)
    +        if free_disk_space <= 10:
    +            j.logger.warning("[Admin Package - Disk Check Service] Your free disk space is less than 10 GBs")
    +            j.tools.notificationsqueue.push(
    +                "Your free disk space is less than 10 GBs", category="Health check", level=LEVEL.WARNING
    +            )
    +
    +
    +service = DiskCheckService()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class DiskCheckService +(interval='* * * * *', *args, **kwargs) +
    +
    +

    Helper class that provides a standard way to create an ABC using +inheritance.

    +

    Check disk space every 1 minute

    +
    + +Expand source code + +
    class DiskCheckService(BackgroundService):
    +    def __init__(self, interval="* * * * *", *args, **kwargs):
    +        """
    +            Check disk space every 1 minute
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def job(self):
    +        disk_obj = j.sals.fs.shutil.disk_usage("/")
    +        free_disk_space = disk_obj.free // (1024.0 ** 3)
    +        if free_disk_space <= 10:
    +            j.logger.warning("[Admin Package - Disk Check Service] Your free disk space is less than 10 GBs")
    +            j.tools.notificationsqueue.push(
    +                "Your free disk space is less than 10 GBs", category="Health check", level=LEVEL.WARNING
    +            )
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/services/heartbeat.html b/docs/api/jumpscale/packages/admin/services/heartbeat.html new file mode 100644 index 000000000..55c5c68cb --- /dev/null +++ b/docs/api/jumpscale/packages/admin/services/heartbeat.html @@ -0,0 +1,188 @@ + + + + + + +jumpscale.packages.admin.services.heartbeat API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.services.heartbeat

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +import requests
    +import os
    +import binascii
    +from io import StringIO
    +import urllib.parse
    +
    +VDC_NAME = os.environ.get("VDC_NAME", "")
    +MONITORING_SERVER_URL = os.environ.get("MONITORING_SERVER_URL")
    +
    +
    +class HeartBeatService(BackgroundService):
    +    # def __init__(self, interval=60 * 10, *args, **kwargs):
    +    def __init__(self, interval=10, *args, **kwargs):
    +        """
    +        Check disk space every 10 seconds
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def _encode_data(self, data):
    +        keys = [
    +            "tid",
    +            "tname",
    +            "vdc_name",
    +        ]
    +        b = StringIO()
    +        for key in keys:
    +            if key in data:
    +                b.write(key)
    +                b.write(str(data.get(key)))
    +        return b.getvalue().encode()
    +
    +    def job(self):
    +        if MONITORING_SERVER_URL:
    +            identity_name = j.core.identity.me.instance_name
    +            tid = j.core.identity.get(identity_name).tid
    +            data = {
    +                "tid": tid,
    +                "vdc_name": VDC_NAME,
    +                "tname": os.environ.get("VDC_OWNER_TNAME"),
    +            }
    +            encoded_data = self._encode_data(data)
    +            signature = j.core.identity.me.nacl.signing_key.sign(encoded_data).signature
    +
    +            data["signature"] = binascii.hexlify(signature).decode()
    +            url = urllib.parse.urljoin(MONITORING_SERVER_URL, "heartbeat")
    +            try:
    +                requests.post(url, json=data, headers={"Content-type": "application/json"})
    +            except Exception as e:
    +                j.logger.error(f"Failed to send heartbeat, URL:{url}, exception: {str(e)}")
    +
    +
    +service = HeartBeatService()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class HeartBeatService +(interval=10, *args, **kwargs) +
    +
    +

    Helper class that provides a standard way to create an ABC using +inheritance.

    +

    Check disk space every 10 seconds

    +
    + +Expand source code + +
    class HeartBeatService(BackgroundService):
    +    # def __init__(self, interval=60 * 10, *args, **kwargs):
    +    def __init__(self, interval=10, *args, **kwargs):
    +        """
    +        Check disk space every 10 seconds
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def _encode_data(self, data):
    +        keys = [
    +            "tid",
    +            "tname",
    +            "vdc_name",
    +        ]
    +        b = StringIO()
    +        for key in keys:
    +            if key in data:
    +                b.write(key)
    +                b.write(str(data.get(key)))
    +        return b.getvalue().encode()
    +
    +    def job(self):
    +        if MONITORING_SERVER_URL:
    +            identity_name = j.core.identity.me.instance_name
    +            tid = j.core.identity.get(identity_name).tid
    +            data = {
    +                "tid": tid,
    +                "vdc_name": VDC_NAME,
    +                "tname": os.environ.get("VDC_OWNER_TNAME"),
    +            }
    +            encoded_data = self._encode_data(data)
    +            signature = j.core.identity.me.nacl.signing_key.sign(encoded_data).signature
    +
    +            data["signature"] = binascii.hexlify(signature).decode()
    +            url = urllib.parse.urljoin(MONITORING_SERVER_URL, "heartbeat")
    +            try:
    +                requests.post(url, json=data, headers={"Content-type": "application/json"})
    +            except Exception as e:
    +                j.logger.error(f"Failed to send heartbeat, URL:{url}, exception: {str(e)}")
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/services/index.html b/docs/api/jumpscale/packages/admin/services/index.html new file mode 100644 index 000000000..b1413998e --- /dev/null +++ b/docs/api/jumpscale/packages/admin/services/index.html @@ -0,0 +1,85 @@ + + + + + + +jumpscale.packages.admin.services API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/services/notifier.html b/docs/api/jumpscale/packages/admin/services/notifier.html new file mode 100644 index 000000000..3fd566955 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/services/notifier.html @@ -0,0 +1,253 @@ + + + + + + +jumpscale.packages.admin.services.notifier API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.services.notifier

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +
    +from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +
    +MAIL_QUEUE = "MAIL_QUEUE"
    +TELEGRAM_QUEUE = "TELEGRAM_QUEUE"
    +
    +
    +class Notifier(BackgroundService):
    +    def __init__(self, interval=60 * 5, *args, **kwargs):
    +        """
    +        Notifier service is used to send notifications using different notifications system.
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def job(self):
    +        self.mail_notifier()
    +
    +    def mail_notifier(self):
    +        """Pick mails from redis queue and send them.
    +
    +        example:
    +            mail_info = '{"recipients_emails": ["ashraf@codescalers.com"], "sender": "hanafy@codescalers.com", "subject": "Mail Notifier", "message": "Trying mail notifier"}'
    +            j.core.db.rpush("MAIL_QUEUE", mail_info)
    +        """
    +        while True:
    +            mail_info_json = j.core.db.lpop(MAIL_QUEUE)
    +            if not mail_info_json:
    +                break
    +            try:
    +                mail_info = j.data.serializers.json.loads(mail_info_json.decode())
    +                recipients_emails = mail_info["recipients_emails"]
    +                message = mail_info["message"]
    +                sender = mail_info["sender"]
    +                subject = mail_info["subject"]
    +                self.send_mail(recipients_emails=recipients_emails, subject=subject, message=message, sender=sender)
    +            except Exception as e:
    +                j.logger.exception(f"Failed to send mail: {mail_info_json}", exception=e)
    +
    +    def send_mail(self, recipients_emails, sender, subject, message):
    +        recipients_emails = recipients_emails or []
    +        if isinstance(recipients_emails, str):
    +            recipients_emails = [recipients_emails]
    +        mail = j.clients.mail.get("mail")
    +        mail_config = j.core.config.get("EMAIL_SERVER_CONFIG")
    +        mail.smtp_server = mail_config.get("host")
    +        mail.smtp_port = mail_config.get("port")
    +        mail.login = mail_config.get("username")
    +        mail.password = mail_config.get("password")
    +        mail.send(recipients_emails, sender=sender, subject=subject, message=message)
    +
    +
    +service = Notifier()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Notifier +(interval=300, *args, **kwargs) +
    +
    +

    Helper class that provides a standard way to create an ABC using +inheritance.

    +

    Notifier service is used to send notifications using different notifications system.

    +
    + +Expand source code + +
    class Notifier(BackgroundService):
    +    def __init__(self, interval=60 * 5, *args, **kwargs):
    +        """
    +        Notifier service is used to send notifications using different notifications system.
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def job(self):
    +        self.mail_notifier()
    +
    +    def mail_notifier(self):
    +        """Pick mails from redis queue and send them.
    +
    +        example:
    +            mail_info = '{"recipients_emails": ["ashraf@codescalers.com"], "sender": "hanafy@codescalers.com", "subject": "Mail Notifier", "message": "Trying mail notifier"}'
    +            j.core.db.rpush("MAIL_QUEUE", mail_info)
    +        """
    +        while True:
    +            mail_info_json = j.core.db.lpop(MAIL_QUEUE)
    +            if not mail_info_json:
    +                break
    +            try:
    +                mail_info = j.data.serializers.json.loads(mail_info_json.decode())
    +                recipients_emails = mail_info["recipients_emails"]
    +                message = mail_info["message"]
    +                sender = mail_info["sender"]
    +                subject = mail_info["subject"]
    +                self.send_mail(recipients_emails=recipients_emails, subject=subject, message=message, sender=sender)
    +            except Exception as e:
    +                j.logger.exception(f"Failed to send mail: {mail_info_json}", exception=e)
    +
    +    def send_mail(self, recipients_emails, sender, subject, message):
    +        recipients_emails = recipients_emails or []
    +        if isinstance(recipients_emails, str):
    +            recipients_emails = [recipients_emails]
    +        mail = j.clients.mail.get("mail")
    +        mail_config = j.core.config.get("EMAIL_SERVER_CONFIG")
    +        mail.smtp_server = mail_config.get("host")
    +        mail.smtp_port = mail_config.get("port")
    +        mail.login = mail_config.get("username")
    +        mail.password = mail_config.get("password")
    +        mail.send(recipients_emails, sender=sender, subject=subject, message=message)
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def mail_notifier(self) +
    +
    +

    Pick mails from redis queue and send them.

    +

    example: +mail_info = '{"recipients_emails": ["ashraf@codescalers.com"], "sender": "hanafy@codescalers.com", "subject": "Mail Notifier", "message": "Trying mail notifier"}' +j.core.db.rpush("MAIL_QUEUE", mail_info)

    +
    + +Expand source code + +
    def mail_notifier(self):
    +    """Pick mails from redis queue and send them.
    +
    +    example:
    +        mail_info = '{"recipients_emails": ["ashraf@codescalers.com"], "sender": "hanafy@codescalers.com", "subject": "Mail Notifier", "message": "Trying mail notifier"}'
    +        j.core.db.rpush("MAIL_QUEUE", mail_info)
    +    """
    +    while True:
    +        mail_info_json = j.core.db.lpop(MAIL_QUEUE)
    +        if not mail_info_json:
    +            break
    +        try:
    +            mail_info = j.data.serializers.json.loads(mail_info_json.decode())
    +            recipients_emails = mail_info["recipients_emails"]
    +            message = mail_info["message"]
    +            sender = mail_info["sender"]
    +            subject = mail_info["subject"]
    +            self.send_mail(recipients_emails=recipients_emails, subject=subject, message=message, sender=sender)
    +        except Exception as e:
    +            j.logger.exception(f"Failed to send mail: {mail_info_json}", exception=e)
    +
    +
    +
    +def send_mail(self, recipients_emails, sender, subject, message) +
    +
    +
    +
    + +Expand source code + +
    def send_mail(self, recipients_emails, sender, subject, message):
    +    recipients_emails = recipients_emails or []
    +    if isinstance(recipients_emails, str):
    +        recipients_emails = [recipients_emails]
    +    mail = j.clients.mail.get("mail")
    +    mail_config = j.core.config.get("EMAIL_SERVER_CONFIG")
    +    mail.smtp_server = mail_config.get("host")
    +    mail.smtp_port = mail_config.get("port")
    +    mail.login = mail_config.get("username")
    +    mail.password = mail_config.get("password")
    +    mail.send(recipients_emails, sender=sender, subject=subject, message=message)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/admin/services/stellar.html b/docs/api/jumpscale/packages/admin/services/stellar.html new file mode 100644 index 000000000..4c38da1c1 --- /dev/null +++ b/docs/api/jumpscale/packages/admin/services/stellar.html @@ -0,0 +1,166 @@ + + + + + + +jumpscale.packages.admin.services.stellar API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.admin.services.stellar

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +from jumpscale.tools.notificationsqueue.queue import LEVEL
    +from crontab import CronTab
    +
    +
    +class StellarService(BackgroundService):
    +    def __init__(self, interval=CronTab("* * * * *"), *args, **kwargs):
    +        """
    +            Check stellar service state every 1 min
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +        self.stellar_state = True
    +
    +    def job(self):
    +        # Check 3 times to make sure service is down
    +        retries = 3
    +        current_state = None
    +        while retries:
    +            try:
    +                current_state = j.clients.stellar.check_stellar_service()
    +            except Exception:
    +                current_state = False
    +            if current_state:
    +                break
    +            retries -= 1
    +
    +        if current_state != self.stellar_state:
    +            self.stellar_state = current_state
    +            if current_state:
    +                j.logger.info("[Admin Package - Stellar Service] Stellar services are now reachable")
    +                j.tools.notificationsqueue.push(
    +                    "Stellar services are now reachable", category="Stellar", level=LEVEL.INFO
    +                )
    +            else:
    +                j.logger.error("[Admin Package - Stellar Service] Could not reach stellar")
    +                j.tools.notificationsqueue.push("Could not reach stellar", category="Stellar", level=LEVEL.ERROR)
    +
    +
    +service = StellarService()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class StellarService +(interval=<crontab._crontab.CronTab object>, *args, **kwargs) +
    +
    +

    Helper class that provides a standard way to create an ABC using +inheritance.

    +

    Check stellar service state every 1 min

    +
    + +Expand source code + +
    class StellarService(BackgroundService):
    +    def __init__(self, interval=CronTab("* * * * *"), *args, **kwargs):
    +        """
    +            Check stellar service state every 1 min
    +        """
    +        super().__init__(interval, *args, **kwargs)
    +        self.stellar_state = True
    +
    +    def job(self):
    +        # Check 3 times to make sure service is down
    +        retries = 3
    +        current_state = None
    +        while retries:
    +            try:
    +                current_state = j.clients.stellar.check_stellar_service()
    +            except Exception:
    +                current_state = False
    +            if current_state:
    +                break
    +            retries -= 1
    +
    +        if current_state != self.stellar_state:
    +            self.stellar_state = current_state
    +            if current_state:
    +                j.logger.info("[Admin Package - Stellar Service] Stellar services are now reachable")
    +                j.tools.notificationsqueue.push(
    +                    "Stellar services are now reachable", category="Stellar", level=LEVEL.INFO
    +                )
    +            else:
    +                j.logger.error("[Admin Package - Stellar Service] Could not reach stellar")
    +                j.tools.notificationsqueue.push("Could not reach stellar", category="Stellar", level=LEVEL.ERROR)
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/auth/bottle/auth.html b/docs/api/jumpscale/packages/auth/bottle/auth.html new file mode 100644 index 000000000..1fc34c6bc --- /dev/null +++ b/docs/api/jumpscale/packages/auth/bottle/auth.html @@ -0,0 +1,981 @@ + + + + + + +jumpscale.packages.auth.bottle.auth API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.auth.bottle.auth

    +
    +
    +
    + +Expand source code + +
    from functools import wraps
    +from json import JSONDecodeError
    +from urllib.parse import urlencode, quote, unquote
    +
    +import nacl
    +import requests
    +from bottle import Bottle, request, abort, redirect, response
    +from nacl.public import Box
    +from nacl.signing import VerifyKey
    +
    +from jumpscale.loader import j
    +
    +REDIRECT_URL = "https://login.threefold.me"
    +CALLBACK_URL = "/auth/3bot_callback"
    +LOGIN_URL = "/auth/login"
    +
    +app = Bottle()
    +
    +
    +@app.hook("before_request")
    +def setup_request():
    +    request.session = request.environ.get("beaker.session", {})
    +
    +
    +templates_path = j.sals.fs.join_paths(j.sals.fs.dirname(__file__), "templates")
    +env = j.tools.jinja2.get_env(templates_path)
    +
    +
    +@app.route("/login")
    +def login():
    +    """List available providers for login and redirect to the selected provider (ThreeFold Connect)
    +
    +    Returns:
    +        Renders the template of login page
    +    """
    +    session = request.environ.get("beaker.session", {})
    +    provider = request.query.get("provider")
    +    next_url = quote(request.query.get("next_url", session.get("next_url", "/")))
    +
    +    if provider and provider == "3bot":
    +        state = j.data.idgenerator.chars(20)
    +        session["next_url"] = next_url
    +        session["state"] = state
    +        app_id = request.get_header("host")
    +        params = {
    +            "state": state,
    +            "appid": app_id,
    +            "scope": j.data.serializers.json.dumps({"user": True, "email": True, "walletAddress": True}),
    +            "redirecturl": CALLBACK_URL,
    +            "publickey": j.core.identity.me.nacl.public_key.encode(encoder=nacl.encoding.Base64Encoder),
    +        }
    +        params = urlencode(params)
    +        return redirect(f"{REDIRECT_URL}?{params}", code=302)
    +
    +    return env.get_template("login.html").render(providers=[], next_url=next_url)
    +
    +
    +@app.route("/3bot_callback")
    +def callback():
    +    """Takes the response from the provider and verify the identity of the logged in user
    +
    +    Returns:
    +        Redirect to the wanted page after authentication
    +    """
    +    session = request.environ.get("beaker.session")
    +    data = request.query.get("signedAttempt")
    +
    +    if not data:
    +        return abort(400, "signedAttempt parameter is missing")
    +
    +    data = j.data.serializers.json.loads(data)
    +
    +    if "signedAttempt" not in data:
    +        return abort(400, "signedAttempt value is missing")
    +
    +    username = data["doubleName"]
    +
    +    if not username:
    +        return abort(400, "DoubleName is missing")
    +
    +    res = requests.get(f"https://login.threefold.me/api/users/{username}", {"Content-Type": "application/json"})
    +    if res.status_code != 200:
    +        return abort(400, "Error getting user pub key")
    +    pub_key = res.json()["publicKey"]
    +
    +    user_pub_key = VerifyKey(j.data.serializers.base64.decode(pub_key))
    +
    +    # verify data
    +    signedData = data["signedAttempt"]
    +
    +    verifiedData = user_pub_key.verify(j.data.serializers.base64.decode(signedData)).decode()
    +
    +    data = j.data.serializers.json.loads(verifiedData)
    +
    +    if "doubleName" not in data:
    +        return abort(400, "Decrypted data does not contain (doubleName)")
    +
    +    if "signedState" not in data:
    +        return abort(400, "Decrypted data does not contain (state)")
    +
    +    if data["doubleName"] != username:
    +        return abort(400, "username mismatch!")
    +
    +    # verify state
    +    state = data["signedState"]
    +    if state != session["state"]:
    +        return abort(400, "Invalid state. not matching one in user session")
    +
    +    nonce = j.data.serializers.base64.decode(data["data"]["nonce"])
    +    ciphertext = j.data.serializers.base64.decode(data["data"]["ciphertext"])
    +
    +    try:
    +        priv = j.core.identity.me.nacl.private_key
    +        box = Box(priv, user_pub_key.to_curve25519_public_key())
    +        decrypted = box.decrypt(ciphertext, nonce)
    +    except nacl.exceptions.CryptoError:
    +        return abort(400, "Error decrypting data")
    +
    +    try:
    +        result = j.data.serializers.json.loads(decrypted)
    +    except JSONDecodeError:
    +        return abort(400, "3Bot login returned faulty data")
    +
    +    if "email" not in result:
    +        return abort(400, "Email is not present in data")
    +
    +    email = result["email"]["email"]
    +
    +    sei = result["email"]["sei"]
    +    res = requests.post(
    +        "https://openkyc.live/verification/verify-sei",
    +        headers={"Content-Type": "application/json"},
    +        json={"signedEmailIdentifier": sei},
    +    )
    +
    +    if res.status_code != 200:
    +        next_url = request.query.get("next_url", "/auth/logout")
    +        return env.get_template("email_not_verified.html").render(next_url=next_url)
    +
    +    username = username.lower()  # workaround usernames that are returned by signed attempt with camel cases
    +    session["username"] = username
    +    session["email"] = email
    +    session["authorized"] = True
    +    session["signedAttempt"] = signedData
    +    if result.get("walletAddressData"):
    +        session["walletAddress"] = result.get("walletAddressData").get("address")
    +
    +    return redirect(unquote(session.get("next_url", "/")))
    +
    +
    +@app.route("/logout")
    +def logout():
    +    """Invalidates the user session and redirect to login page
    +
    +    Returns:
    +        Redirect to the login page
    +    """
    +    session = request.environ.get("beaker.session", {})
    +    try:
    +        session.invalidate()
    +    except AttributeError:
    +        pass
    +
    +    next_url = request.query.get("next_url", "/")
    +    return redirect(f"{LOGIN_URL}?next_url={next_url}")
    +
    +
    +@app.route("/accessdenied")
    +def access_denied():
    +    """Displays the access denied page when the authenticated user is not authorized to view the page
    +
    +    Returns:
    +        Renders access denied page
    +    """
    +    email = request.environ.get("beaker.session").get("email")
    +    next_url = request.query.get("next_url", "/auth/logout")
    +    return env.get_template("access_denied.html").render(email=email, next_url=next_url)
    +
    +
    +@app.route("/auto_login")
    +def auto_login():
    +    """Auto login is used for testing for skipping login and use the current user's info instead.
    +
    +    Returns:
    +        Redirect to the admin dashboard.
    +    """
    +    if j.core.config.get("AUTO_LOGIN"):
    +        session = request.environ.get("beaker.session", {})
    +        session["username"] = j.core.identity.me.tname
    +        session["email"] = j.core.identity.me.email
    +        session["authorized"] = True
    +        session["walletAddress"] = ""
    +
    +    return redirect("/admin")
    +
    +
    +def get_user_info():
    +    """Parse user information from the session object
    +
    +    Returns:
    +        [JSON string]: [user information session]
    +    """
    +
    +    def _valid(tname, temail):
    +        if not j.core.identity.is_configured:
    +            return False
    +        if tname != j.core.identity.me.tname:
    +            return False
    +        if temail != j.core.identity.me.email:
    +            return False
    +        return True
    +
    +    session = request.environ.get("beaker.session", {})
    +    tname = session.get("username", "")
    +    temail = session.get("email", "")
    +    tid = session.get("tid", 0)
    +    wallet_address = session.get("walletAddress", "")
    +
    +    # update tid in session when the identity changes
    +    devmode = not j.core.identity.is_configured
    +    if not devmode and not _valid(tname, temail):
    +        session["tid"] = None
    +
    +    session.get("signedAttempt", "")
    +    response.content_type = "application/json"
    +    return j.data.serializers.json.dumps(
    +        {
    +            "username": tname.lower(),
    +            "email": temail.lower(),
    +            "tid": tid,
    +            "devmode": not j.core.config.get_config().get("threebot_connect", True),
    +            "walletAddress": wallet_address,
    +        }
    +    )
    +
    +
    +def is_admin(tname):
    +    """Checks if the user provided is considered an admin or not
    +
    +    Args:
    +        tname (str): threebot name
    +
    +    Returns:
    +        [Bool]: [True if the user is an admin]
    +    """
    +    if j.core.identity.is_configured:
    +        threebot_me = j.core.identity.me
    +        return threebot_me.tname == tname or tname in threebot_me.admins
    +    else:
    +        return True
    +
    +
    +def authenticated(handler):
    +    """decorator for the methods to be for authenticated users only
    +
    +    Args:
    +        handler (method)
    +    """
    +
    +    def decorator(*args, **kwargs):
    +        session = request.environ.get("beaker.session", {})
    +        if j.core.config.get_config().get("threebot_connect", True) and j.core.identity.is_configured:
    +            if not session.get("authorized", False):
    +                return abort(401)
    +        return handler(*args, **kwargs)
    +
    +    return decorator
    +
    +
    +def admin_only(handler):
    +    """decorator for methods for admin access only
    +
    +    Args:
    +        handler (method)
    +    """
    +
    +    def decorator(*args, **kwargs):
    +        session = request.environ.get("beaker.session")
    +        if j.core.config.get_config().get("threebot_connect", True):
    +            username = session.get("username")
    +            if not is_admin(username):
    +                return abort(403)
    +
    +        return handler(*args, **kwargs)
    +
    +    return decorator
    +
    +
    +@app.route("/authenticated")
    +@authenticated
    +def is_authenticated():
    +    """get user information if it is authenticated
    +
    +    Returns:
    +        [JSON string]: [user information session]
    +    """
    +    return get_user_info()
    +
    +
    +@app.route("/authorized")
    +@authenticated
    +@admin_only
    +def is_authorized():
    +    """get user information if it is authenticated and authorized as admin only
    +
    +    Returns:
    +        [JSON string]: [user information session]
    +    """
    +    return get_user_info()
    +
    +
    +@app.route("/package_authorized/<package_name>")
    +@authenticated
    +def is_package_authorized(package_name):
    +    """
    +    get user information if it is authorized user in the package config
    +
    +    Returns:
    +        [JSON string]: [user information session]
    +    """
    +    authorized_users = get_package_admins(package_name)
    +    user_info = get_user_info()
    +    user_dict = j.data.serializers.json.loads(user_info)
    +    username = user_dict["username"]
    +    # if the package doesn't include admins then allow any authenticated user
    +    if authorized_users and not any([username in authorized_users, username in j.core.identity.me.admins]):
    +        return abort(403)
    +    return user_info
    +
    +
    +def package_authorized(package_name):
    +    def decorator(function):
    +        def wrapper(*args, **kwargs):
    +            authorized_users = get_package_admins(package_name)
    +            session = request.environ.get("beaker.session")
    +            username = session.get("username")
    +            if authorized_users and not any([username in authorized_users, username in j.core.identity.me.admins]):
    +                return abort(403)
    +            return function(*args, **kwargs)
    +
    +        return wrapper
    +
    +    return decorator
    +
    +
    +def login_required(func):
    +    """Decorator for the methods we want to secure
    +
    +    Args:
    +        func (method)
    +    """
    +
    +    @wraps(func)
    +    def decorator(*args, **kwargs):
    +        session = request.environ.get("beaker.session", {})
    +        if j.core.config.get_config().get("threebot_connect", True):
    +            if not session.get("authorized", False):
    +                session["next_url"] = request.url
    +                return redirect(LOGIN_URL)
    +        return func(*args, **kwargs)
    +
    +    return decorator
    +
    +
    +def add_package_user(package_name, username):
    +    package = j.servers.threebot.default.packages.get(package_name)
    +    if not package:
    +        raise j.exceptions.Validation(f"can't add admin to non installed package {package_name}")
    +    package.admins.append(username)
    +    j.servers.threebot.default.packages.save()
    +
    +
    +def get_package_admins(package_name):
    +    package = j.servers.threebot.default.packages.get(package_name)
    +    if not package:
    +        raise j.exceptions.Validation(f"package {package_name} is not installed")
    +    return package.admins
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def access_denied() +
    +
    +

    Displays the access denied page when the authenticated user is not authorized to view the page

    +

    Returns

    +

    Renders access denied page

    +
    + +Expand source code + +
    @app.route("/accessdenied")
    +def access_denied():
    +    """Displays the access denied page when the authenticated user is not authorized to view the page
    +
    +    Returns:
    +        Renders access denied page
    +    """
    +    email = request.environ.get("beaker.session").get("email")
    +    next_url = request.query.get("next_url", "/auth/logout")
    +    return env.get_template("access_denied.html").render(email=email, next_url=next_url)
    +
    +
    +
    +def add_package_user(package_name, username) +
    +
    +
    +
    + +Expand source code + +
    def add_package_user(package_name, username):
    +    package = j.servers.threebot.default.packages.get(package_name)
    +    if not package:
    +        raise j.exceptions.Validation(f"can't add admin to non installed package {package_name}")
    +    package.admins.append(username)
    +    j.servers.threebot.default.packages.save()
    +
    +
    +
    +def admin_only(handler) +
    +
    +

    decorator for methods for admin access only

    +

    Args

    +

    handler (method)

    +
    + +Expand source code + +
    def admin_only(handler):
    +    """decorator for methods for admin access only
    +
    +    Args:
    +        handler (method)
    +    """
    +
    +    def decorator(*args, **kwargs):
    +        session = request.environ.get("beaker.session")
    +        if j.core.config.get_config().get("threebot_connect", True):
    +            username = session.get("username")
    +            if not is_admin(username):
    +                return abort(403)
    +
    +        return handler(*args, **kwargs)
    +
    +    return decorator
    +
    +
    +
    +def authenticated(handler) +
    +
    +

    decorator for the methods to be for authenticated users only

    +

    Args

    +

    handler (method)

    +
    + +Expand source code + +
    def authenticated(handler):
    +    """decorator for the methods to be for authenticated users only
    +
    +    Args:
    +        handler (method)
    +    """
    +
    +    def decorator(*args, **kwargs):
    +        session = request.environ.get("beaker.session", {})
    +        if j.core.config.get_config().get("threebot_connect", True) and j.core.identity.is_configured:
    +            if not session.get("authorized", False):
    +                return abort(401)
    +        return handler(*args, **kwargs)
    +
    +    return decorator
    +
    +
    +
    +def auto_login() +
    +
    +

    Auto login is used for testing for skipping login and use the current user's info instead.

    +

    Returns

    +

    Redirect to the admin dashboard.

    +
    + +Expand source code + +
    @app.route("/auto_login")
    +def auto_login():
    +    """Auto login is used for testing for skipping login and use the current user's info instead.
    +
    +    Returns:
    +        Redirect to the admin dashboard.
    +    """
    +    if j.core.config.get("AUTO_LOGIN"):
    +        session = request.environ.get("beaker.session", {})
    +        session["username"] = j.core.identity.me.tname
    +        session["email"] = j.core.identity.me.email
    +        session["authorized"] = True
    +        session["walletAddress"] = ""
    +
    +    return redirect("/admin")
    +
    +
    +
    +def callback() +
    +
    +

    Takes the response from the provider and verify the identity of the logged in user

    +

    Returns

    +

    Redirect to the wanted page after authentication

    +
    + +Expand source code + +
    @app.route("/3bot_callback")
    +def callback():
    +    """Takes the response from the provider and verify the identity of the logged in user
    +
    +    Returns:
    +        Redirect to the wanted page after authentication
    +    """
    +    session = request.environ.get("beaker.session")
    +    data = request.query.get("signedAttempt")
    +
    +    if not data:
    +        return abort(400, "signedAttempt parameter is missing")
    +
    +    data = j.data.serializers.json.loads(data)
    +
    +    if "signedAttempt" not in data:
    +        return abort(400, "signedAttempt value is missing")
    +
    +    username = data["doubleName"]
    +
    +    if not username:
    +        return abort(400, "DoubleName is missing")
    +
    +    res = requests.get(f"https://login.threefold.me/api/users/{username}", {"Content-Type": "application/json"})
    +    if res.status_code != 200:
    +        return abort(400, "Error getting user pub key")
    +    pub_key = res.json()["publicKey"]
    +
    +    user_pub_key = VerifyKey(j.data.serializers.base64.decode(pub_key))
    +
    +    # verify data
    +    signedData = data["signedAttempt"]
    +
    +    verifiedData = user_pub_key.verify(j.data.serializers.base64.decode(signedData)).decode()
    +
    +    data = j.data.serializers.json.loads(verifiedData)
    +
    +    if "doubleName" not in data:
    +        return abort(400, "Decrypted data does not contain (doubleName)")
    +
    +    if "signedState" not in data:
    +        return abort(400, "Decrypted data does not contain (state)")
    +
    +    if data["doubleName"] != username:
    +        return abort(400, "username mismatch!")
    +
    +    # verify state
    +    state = data["signedState"]
    +    if state != session["state"]:
    +        return abort(400, "Invalid state. not matching one in user session")
    +
    +    nonce = j.data.serializers.base64.decode(data["data"]["nonce"])
    +    ciphertext = j.data.serializers.base64.decode(data["data"]["ciphertext"])
    +
    +    try:
    +        priv = j.core.identity.me.nacl.private_key
    +        box = Box(priv, user_pub_key.to_curve25519_public_key())
    +        decrypted = box.decrypt(ciphertext, nonce)
    +    except nacl.exceptions.CryptoError:
    +        return abort(400, "Error decrypting data")
    +
    +    try:
    +        result = j.data.serializers.json.loads(decrypted)
    +    except JSONDecodeError:
    +        return abort(400, "3Bot login returned faulty data")
    +
    +    if "email" not in result:
    +        return abort(400, "Email is not present in data")
    +
    +    email = result["email"]["email"]
    +
    +    sei = result["email"]["sei"]
    +    res = requests.post(
    +        "https://openkyc.live/verification/verify-sei",
    +        headers={"Content-Type": "application/json"},
    +        json={"signedEmailIdentifier": sei},
    +    )
    +
    +    if res.status_code != 200:
    +        next_url = request.query.get("next_url", "/auth/logout")
    +        return env.get_template("email_not_verified.html").render(next_url=next_url)
    +
    +    username = username.lower()  # workaround usernames that are returned by signed attempt with camel cases
    +    session["username"] = username
    +    session["email"] = email
    +    session["authorized"] = True
    +    session["signedAttempt"] = signedData
    +    if result.get("walletAddressData"):
    +        session["walletAddress"] = result.get("walletAddressData").get("address")
    +
    +    return redirect(unquote(session.get("next_url", "/")))
    +
    +
    +
    +def get_package_admins(package_name) +
    +
    +
    +
    + +Expand source code + +
    def get_package_admins(package_name):
    +    package = j.servers.threebot.default.packages.get(package_name)
    +    if not package:
    +        raise j.exceptions.Validation(f"package {package_name} is not installed")
    +    return package.admins
    +
    +
    +
    +def get_user_info() +
    +
    +

    Parse user information from the session object

    +

    Returns

    +
    +
    [JSON string]
    +
    [user information session]
    +
    +
    + +Expand source code + +
    def get_user_info():
    +    """Parse user information from the session object
    +
    +    Returns:
    +        [JSON string]: [user information session]
    +    """
    +
    +    def _valid(tname, temail):
    +        if not j.core.identity.is_configured:
    +            return False
    +        if tname != j.core.identity.me.tname:
    +            return False
    +        if temail != j.core.identity.me.email:
    +            return False
    +        return True
    +
    +    session = request.environ.get("beaker.session", {})
    +    tname = session.get("username", "")
    +    temail = session.get("email", "")
    +    tid = session.get("tid", 0)
    +    wallet_address = session.get("walletAddress", "")
    +
    +    # update tid in session when the identity changes
    +    devmode = not j.core.identity.is_configured
    +    if not devmode and not _valid(tname, temail):
    +        session["tid"] = None
    +
    +    session.get("signedAttempt", "")
    +    response.content_type = "application/json"
    +    return j.data.serializers.json.dumps(
    +        {
    +            "username": tname.lower(),
    +            "email": temail.lower(),
    +            "tid": tid,
    +            "devmode": not j.core.config.get_config().get("threebot_connect", True),
    +            "walletAddress": wallet_address,
    +        }
    +    )
    +
    +
    +
    +def is_admin(tname) +
    +
    +

    Checks if the user provided is considered an admin or not

    +

    Args

    +
    +
    tname : str
    +
    threebot name
    +
    +

    Returns

    +
    +
    [Bool]
    +
    [True if the user is an admin]
    +
    +
    + +Expand source code + +
    def is_admin(tname):
    +    """Checks if the user provided is considered an admin or not
    +
    +    Args:
    +        tname (str): threebot name
    +
    +    Returns:
    +        [Bool]: [True if the user is an admin]
    +    """
    +    if j.core.identity.is_configured:
    +        threebot_me = j.core.identity.me
    +        return threebot_me.tname == tname or tname in threebot_me.admins
    +    else:
    +        return True
    +
    +
    +
    +def is_authenticated(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def decorator(*args, **kwargs):
    +    session = request.environ.get("beaker.session", {})
    +    if j.core.config.get_config().get("threebot_connect", True) and j.core.identity.is_configured:
    +        if not session.get("authorized", False):
    +            return abort(401)
    +    return handler(*args, **kwargs)
    +
    +
    +
    +def is_authorized(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def decorator(*args, **kwargs):
    +    session = request.environ.get("beaker.session", {})
    +    if j.core.config.get_config().get("threebot_connect", True) and j.core.identity.is_configured:
    +        if not session.get("authorized", False):
    +            return abort(401)
    +    return handler(*args, **kwargs)
    +
    +
    +
    +def is_package_authorized(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def decorator(*args, **kwargs):
    +    session = request.environ.get("beaker.session", {})
    +    if j.core.config.get_config().get("threebot_connect", True) and j.core.identity.is_configured:
    +        if not session.get("authorized", False):
    +            return abort(401)
    +    return handler(*args, **kwargs)
    +
    +
    +
    +def login() +
    +
    +

    List available providers for login and redirect to the selected provider (ThreeFold Connect)

    +

    Returns

    +

    Renders the template of login page

    +
    + +Expand source code + +
    @app.route("/login")
    +def login():
    +    """List available providers for login and redirect to the selected provider (ThreeFold Connect)
    +
    +    Returns:
    +        Renders the template of login page
    +    """
    +    session = request.environ.get("beaker.session", {})
    +    provider = request.query.get("provider")
    +    next_url = quote(request.query.get("next_url", session.get("next_url", "/")))
    +
    +    if provider and provider == "3bot":
    +        state = j.data.idgenerator.chars(20)
    +        session["next_url"] = next_url
    +        session["state"] = state
    +        app_id = request.get_header("host")
    +        params = {
    +            "state": state,
    +            "appid": app_id,
    +            "scope": j.data.serializers.json.dumps({"user": True, "email": True, "walletAddress": True}),
    +            "redirecturl": CALLBACK_URL,
    +            "publickey": j.core.identity.me.nacl.public_key.encode(encoder=nacl.encoding.Base64Encoder),
    +        }
    +        params = urlencode(params)
    +        return redirect(f"{REDIRECT_URL}?{params}", code=302)
    +
    +    return env.get_template("login.html").render(providers=[], next_url=next_url)
    +
    +
    +
    +def login_required(func) +
    +
    +

    Decorator for the methods we want to secure

    +

    Args

    +

    func (method)

    +
    + +Expand source code + +
    def login_required(func):
    +    """Decorator for the methods we want to secure
    +
    +    Args:
    +        func (method)
    +    """
    +
    +    @wraps(func)
    +    def decorator(*args, **kwargs):
    +        session = request.environ.get("beaker.session", {})
    +        if j.core.config.get_config().get("threebot_connect", True):
    +            if not session.get("authorized", False):
    +                session["next_url"] = request.url
    +                return redirect(LOGIN_URL)
    +        return func(*args, **kwargs)
    +
    +    return decorator
    +
    +
    +
    +def logout() +
    +
    +

    Invalidates the user session and redirect to login page

    +

    Returns

    +

    Redirect to the login page

    +
    + +Expand source code + +
    @app.route("/logout")
    +def logout():
    +    """Invalidates the user session and redirect to login page
    +
    +    Returns:
    +        Redirect to the login page
    +    """
    +    session = request.environ.get("beaker.session", {})
    +    try:
    +        session.invalidate()
    +    except AttributeError:
    +        pass
    +
    +    next_url = request.query.get("next_url", "/")
    +    return redirect(f"{LOGIN_URL}?next_url={next_url}")
    +
    +
    +
    +def package_authorized(package_name) +
    +
    +
    +
    + +Expand source code + +
    def package_authorized(package_name):
    +    def decorator(function):
    +        def wrapper(*args, **kwargs):
    +            authorized_users = get_package_admins(package_name)
    +            session = request.environ.get("beaker.session")
    +            username = session.get("username")
    +            if authorized_users and not any([username in authorized_users, username in j.core.identity.me.admins]):
    +                return abort(403)
    +            return function(*args, **kwargs)
    +
    +        return wrapper
    +
    +    return decorator
    +
    +
    +
    +def setup_request() +
    +
    +
    +
    + +Expand source code + +
    @app.hook("before_request")
    +def setup_request():
    +    request.session = request.environ.get("beaker.session", {})
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/auth/bottle/index.html b/docs/api/jumpscale/packages/auth/bottle/index.html new file mode 100644 index 000000000..b6361eaf6 --- /dev/null +++ b/docs/api/jumpscale/packages/auth/bottle/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.auth.bottle API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/auth/index.html b/docs/api/jumpscale/packages/auth/index.html new file mode 100644 index 000000000..49f1050c3 --- /dev/null +++ b/docs/api/jumpscale/packages/auth/index.html @@ -0,0 +1,162 @@ + + + + + + +jumpscale.packages.auth API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.auth

    +
    +
    +

    Auth package is responsible for making authentication and authorization using ThreeFold Connect

    +

    Components

    +

    package.toml

    +
      +
    • Conains the nginx locations for the package for template static files under static and reverse proxy
    • +
    +

    Bottle app

    +
      +
    • In bottle/auth.py it has the main methods used to create the authentication
    • +
    +

    Templates

    +
      +
    • In bottle/templates There are 3 templates
    • +
    • home.html: the main structure of the auth page
    • +
    • login.html: the login component shows all available providers to login in with (ThreeFold Connect)
    • +
    • access_denied.html: this page is when the user is not authorized to view this page
    • +
    +

    Authentication flow:

    +
      +
    • +

      You need to put the login_required decorator on the required route to secure +example in chatflow package. +@app.route("/<package_name>") +@login_required +def chats(package_name):

      +
    • +
    • +

      This decorator checks if threebot_connect* is enabled if so then it redirects you to the login url

      +
    • +
    • +

      Then login method takes over, it renders the template takes the required provider and redirects to it

      +
    • +
    • +

      After that the response is back to the callback method, it verify the identity of the user and redirect to the requested url

      +
    • +
    • +

      To enable threebot_connect(enabled by default): j.core.config.set('threebot_connect', True)

      +
    • +
    • To disable threebot_connect: j.core.config.set('threebot_connect', False)
    • +
    +
    + +Expand source code + +
    """
    +# Auth package is responsible for making authentication and authorization using ThreeFold Connect
    +
    +## Components
    +
    +### package.toml
    +
    +- Conains the nginx locations for the package for template static files under `static` and reverse proxy
    +
    +### Bottle app
    +
    +- In `bottle/auth.py` it has the main methods used to create the authentication
    +
    +### Templates
    +
    +- In `bottle/templates` There are 3 templates
    +  - home.html: the main structure of the auth page
    +  - login.html: the login component shows all available providers to login in with (ThreeFold Connect)
    +  - access_denied.html: this page is when the user is not authorized to view this page
    +
    +### Authentication flow:
    +
    +  - You need to put the login_required decorator on the required route to secure
    +    example in chatflow package.
    +    ```
    +    @app.route("/<package_name>")
    +    @login_required
    +    def chats(package_name):
    +    ```
    +
    +  - This decorator checks if threebot_connect* is enabled if so then it redirects you to the login url
    +
    +  - Then `login` method takes over, it renders the template takes the required provider and redirects to it
    +
    +  - After that the response is back to the callback method, it verify the identity of the user and redirect to the requested url
    +
    +* To enable threebot_connect(enabled by default): `j.core.config.set('threebot_connect', True)`
    +* To disable threebot_connect: j.core.config.set('threebot_connect', False)
    +
    +
    +"""
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.packages.auth.bottle
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/backup/actors/index.html b/docs/api/jumpscale/packages/backup/actors/index.html new file mode 100644 index 000000000..ef08a4df5 --- /dev/null +++ b/docs/api/jumpscale/packages/backup/actors/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.backup.actors API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/backup/actors/minio.html b/docs/api/jumpscale/packages/backup/actors/minio.html new file mode 100644 index 000000000..6064a2704 --- /dev/null +++ b/docs/api/jumpscale/packages/backup/actors/minio.html @@ -0,0 +1,627 @@ + + + + + + +jumpscale.packages.backup.actors.minio API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.backup.actors.minio

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +
    +INSTANCE_NAME = "minio_backup"
    +MINIO_URL = "s3:{}/threebotbackup"
    +
    +
    +class MinioBackup(BaseActor):
    +    @actor_method
    +    def repos_exist(self) -> bool:
    +        return INSTANCE_NAME in j.tools.restic.list_all()
    +
    +    @actor_method
    +    def init(self, minio_url, password, access_key, secret_key) -> str:
    +        minio = j.tools.restic.get(INSTANCE_NAME)
    +        minio.repo = MINIO_URL.format(minio_url)
    +        minio.password = password
    +        minio.extra_env = {"AWS_ACCESS_KEY_ID": access_key, "AWS_SECRET_ACCESS_KEY": secret_key}
    +        try:
    +            minio.init_repo()
    +        except Exception as e:
    +            j.tools.restic.delete(INSTANCE_NAME)
    +            raise j.exceptions.Value(f"Couldn't create restic repo with given data {e}")
    +        else:
    +            minio.save()
    +
    +        return j.data.serializers.json.dumps({"data": "backup repos inited"})
    +
    +    def _get_instance(self):
    +        if INSTANCE_NAME not in j.tools.restic.list_all():
    +            raise j.exceptions.Value("Please configure backup first")
    +        return j.tools.restic.get(INSTANCE_NAME)
    +
    +    @actor_method
    +    def backup(self, tags=None) -> str:
    +        if tags:
    +            tags = tags.split(",")
    +        else:
    +            tags = []
    +
    +        tags.append(str(j.data.time.now().timestamp))
    +        instance = self._get_instance()
    +        instance.backup(j.core.dirs.JSCFGDIR, tags=tags)
    +        return j.data.serializers.json.dumps({"data": "backup done"})
    +
    +    @actor_method
    +    def snapshots(self, tags=None) -> str:
    +        if tags:
    +            tags = tags.split(",")
    +        instance = self._get_instance()
    +        result = list(reversed(instance.list_snapshots(tags=tags)))
    +        return j.data.serializers.json.dumps({"data": result})
    +
    +    @actor_method
    +    def restore(self) -> str:
    +        instance = self._get_instance()
    +        instance.restore("/")
    +        return j.data.serializers.json.dumps({"data": "data restored"})
    +
    +    @actor_method
    +    def enable_auto_backup(self) -> str:
    +        instance = self._get_instance()
    +        instance.auto_backup(j.core.dirs.JSCFGDIR)
    +        return j.data.serializers.json.dumps({"data": "auto backup enabled"})
    +
    +    @actor_method
    +    def check_auto_backup(self) -> bool:
    +        instance = self._get_instance()
    +        return instance.auto_backup_running(j.core.dirs.JSCFGDIR)
    +
    +    @actor_method
    +    def disable_auto_backup(self) -> str:
    +        instance = self._get_instance()
    +        instance.disable_auto_backup(j.core.dirs.JSCFGDIR)
    +        return j.data.serializers.json.dumps({"data": "auto backup disabled"})
    +
    +
    +Actor = MinioBackup
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class MinioBackup +
    +
    +
    +
    + +Expand source code + +
    class MinioBackup(BaseActor):
    +    @actor_method
    +    def repos_exist(self) -> bool:
    +        return INSTANCE_NAME in j.tools.restic.list_all()
    +
    +    @actor_method
    +    def init(self, minio_url, password, access_key, secret_key) -> str:
    +        minio = j.tools.restic.get(INSTANCE_NAME)
    +        minio.repo = MINIO_URL.format(minio_url)
    +        minio.password = password
    +        minio.extra_env = {"AWS_ACCESS_KEY_ID": access_key, "AWS_SECRET_ACCESS_KEY": secret_key}
    +        try:
    +            minio.init_repo()
    +        except Exception as e:
    +            j.tools.restic.delete(INSTANCE_NAME)
    +            raise j.exceptions.Value(f"Couldn't create restic repo with given data {e}")
    +        else:
    +            minio.save()
    +
    +        return j.data.serializers.json.dumps({"data": "backup repos inited"})
    +
    +    def _get_instance(self):
    +        if INSTANCE_NAME not in j.tools.restic.list_all():
    +            raise j.exceptions.Value("Please configure backup first")
    +        return j.tools.restic.get(INSTANCE_NAME)
    +
    +    @actor_method
    +    def backup(self, tags=None) -> str:
    +        if tags:
    +            tags = tags.split(",")
    +        else:
    +            tags = []
    +
    +        tags.append(str(j.data.time.now().timestamp))
    +        instance = self._get_instance()
    +        instance.backup(j.core.dirs.JSCFGDIR, tags=tags)
    +        return j.data.serializers.json.dumps({"data": "backup done"})
    +
    +    @actor_method
    +    def snapshots(self, tags=None) -> str:
    +        if tags:
    +            tags = tags.split(",")
    +        instance = self._get_instance()
    +        result = list(reversed(instance.list_snapshots(tags=tags)))
    +        return j.data.serializers.json.dumps({"data": result})
    +
    +    @actor_method
    +    def restore(self) -> str:
    +        instance = self._get_instance()
    +        instance.restore("/")
    +        return j.data.serializers.json.dumps({"data": "data restored"})
    +
    +    @actor_method
    +    def enable_auto_backup(self) -> str:
    +        instance = self._get_instance()
    +        instance.auto_backup(j.core.dirs.JSCFGDIR)
    +        return j.data.serializers.json.dumps({"data": "auto backup enabled"})
    +
    +    @actor_method
    +    def check_auto_backup(self) -> bool:
    +        instance = self._get_instance()
    +        return instance.auto_backup_running(j.core.dirs.JSCFGDIR)
    +
    +    @actor_method
    +    def disable_auto_backup(self) -> str:
    +        instance = self._get_instance()
    +        instance.disable_auto_backup(j.core.dirs.JSCFGDIR)
    +        return j.data.serializers.json.dumps({"data": "auto backup disabled"})
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def backup(self, tags=None) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def backup(self, tags=None) -> str:
    +    if tags:
    +        tags = tags.split(",")
    +    else:
    +        tags = []
    +
    +    tags.append(str(j.data.time.now().timestamp))
    +    instance = self._get_instance()
    +    instance.backup(j.core.dirs.JSCFGDIR, tags=tags)
    +    return j.data.serializers.json.dumps({"data": "backup done"})
    +
    +
    +
    +def check_auto_backup(self) ‑> bool +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def check_auto_backup(self) -> bool:
    +    instance = self._get_instance()
    +    return instance.auto_backup_running(j.core.dirs.JSCFGDIR)
    +
    +
    +
    +def disable_auto_backup(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def disable_auto_backup(self) -> str:
    +    instance = self._get_instance()
    +    instance.disable_auto_backup(j.core.dirs.JSCFGDIR)
    +    return j.data.serializers.json.dumps({"data": "auto backup disabled"})
    +
    +
    +
    +def enable_auto_backup(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def enable_auto_backup(self) -> str:
    +    instance = self._get_instance()
    +    instance.auto_backup(j.core.dirs.JSCFGDIR)
    +    return j.data.serializers.json.dumps({"data": "auto backup enabled"})
    +
    +
    +
    +def init(self, minio_url, password, access_key, secret_key) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def init(self, minio_url, password, access_key, secret_key) -> str:
    +    minio = j.tools.restic.get(INSTANCE_NAME)
    +    minio.repo = MINIO_URL.format(minio_url)
    +    minio.password = password
    +    minio.extra_env = {"AWS_ACCESS_KEY_ID": access_key, "AWS_SECRET_ACCESS_KEY": secret_key}
    +    try:
    +        minio.init_repo()
    +    except Exception as e:
    +        j.tools.restic.delete(INSTANCE_NAME)
    +        raise j.exceptions.Value(f"Couldn't create restic repo with given data {e}")
    +    else:
    +        minio.save()
    +
    +    return j.data.serializers.json.dumps({"data": "backup repos inited"})
    +
    +
    +
    +def repos_exist(self) ‑> bool +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def repos_exist(self) -> bool:
    +    return INSTANCE_NAME in j.tools.restic.list_all()
    +
    +
    +
    +def restore(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def restore(self) -> str:
    +    instance = self._get_instance()
    +    instance.restore("/")
    +    return j.data.serializers.json.dumps({"data": "data restored"})
    +
    +
    +
    +def snapshots(self, tags=None) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def snapshots(self, tags=None) -> str:
    +    if tags:
    +        tags = tags.split(",")
    +    instance = self._get_instance()
    +    result = list(reversed(instance.list_snapshots(tags=tags)))
    +    return j.data.serializers.json.dumps({"data": result})
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class MinioBackup(BaseActor):
    +    @actor_method
    +    def repos_exist(self) -> bool:
    +        return INSTANCE_NAME in j.tools.restic.list_all()
    +
    +    @actor_method
    +    def init(self, minio_url, password, access_key, secret_key) -> str:
    +        minio = j.tools.restic.get(INSTANCE_NAME)
    +        minio.repo = MINIO_URL.format(minio_url)
    +        minio.password = password
    +        minio.extra_env = {"AWS_ACCESS_KEY_ID": access_key, "AWS_SECRET_ACCESS_KEY": secret_key}
    +        try:
    +            minio.init_repo()
    +        except Exception as e:
    +            j.tools.restic.delete(INSTANCE_NAME)
    +            raise j.exceptions.Value(f"Couldn't create restic repo with given data {e}")
    +        else:
    +            minio.save()
    +
    +        return j.data.serializers.json.dumps({"data": "backup repos inited"})
    +
    +    def _get_instance(self):
    +        if INSTANCE_NAME not in j.tools.restic.list_all():
    +            raise j.exceptions.Value("Please configure backup first")
    +        return j.tools.restic.get(INSTANCE_NAME)
    +
    +    @actor_method
    +    def backup(self, tags=None) -> str:
    +        if tags:
    +            tags = tags.split(",")
    +        else:
    +            tags = []
    +
    +        tags.append(str(j.data.time.now().timestamp))
    +        instance = self._get_instance()
    +        instance.backup(j.core.dirs.JSCFGDIR, tags=tags)
    +        return j.data.serializers.json.dumps({"data": "backup done"})
    +
    +    @actor_method
    +    def snapshots(self, tags=None) -> str:
    +        if tags:
    +            tags = tags.split(",")
    +        instance = self._get_instance()
    +        result = list(reversed(instance.list_snapshots(tags=tags)))
    +        return j.data.serializers.json.dumps({"data": result})
    +
    +    @actor_method
    +    def restore(self) -> str:
    +        instance = self._get_instance()
    +        instance.restore("/")
    +        return j.data.serializers.json.dumps({"data": "data restored"})
    +
    +    @actor_method
    +    def enable_auto_backup(self) -> str:
    +        instance = self._get_instance()
    +        instance.auto_backup(j.core.dirs.JSCFGDIR)
    +        return j.data.serializers.json.dumps({"data": "auto backup enabled"})
    +
    +    @actor_method
    +    def check_auto_backup(self) -> bool:
    +        instance = self._get_instance()
    +        return instance.auto_backup_running(j.core.dirs.JSCFGDIR)
    +
    +    @actor_method
    +    def disable_auto_backup(self) -> str:
    +        instance = self._get_instance()
    +        instance.disable_auto_backup(j.core.dirs.JSCFGDIR)
    +        return j.data.serializers.json.dumps({"data": "auto backup disabled"})
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def backup(self, tags=None) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def backup(self, tags=None) -> str:
    +    if tags:
    +        tags = tags.split(",")
    +    else:
    +        tags = []
    +
    +    tags.append(str(j.data.time.now().timestamp))
    +    instance = self._get_instance()
    +    instance.backup(j.core.dirs.JSCFGDIR, tags=tags)
    +    return j.data.serializers.json.dumps({"data": "backup done"})
    +
    +
    +
    +def check_auto_backup(self) ‑> bool +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def check_auto_backup(self) -> bool:
    +    instance = self._get_instance()
    +    return instance.auto_backup_running(j.core.dirs.JSCFGDIR)
    +
    +
    +
    +def disable_auto_backup(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def disable_auto_backup(self) -> str:
    +    instance = self._get_instance()
    +    instance.disable_auto_backup(j.core.dirs.JSCFGDIR)
    +    return j.data.serializers.json.dumps({"data": "auto backup disabled"})
    +
    +
    +
    +def enable_auto_backup(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def enable_auto_backup(self) -> str:
    +    instance = self._get_instance()
    +    instance.auto_backup(j.core.dirs.JSCFGDIR)
    +    return j.data.serializers.json.dumps({"data": "auto backup enabled"})
    +
    +
    +
    +def init(self, minio_url, password, access_key, secret_key) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def init(self, minio_url, password, access_key, secret_key) -> str:
    +    minio = j.tools.restic.get(INSTANCE_NAME)
    +    minio.repo = MINIO_URL.format(minio_url)
    +    minio.password = password
    +    minio.extra_env = {"AWS_ACCESS_KEY_ID": access_key, "AWS_SECRET_ACCESS_KEY": secret_key}
    +    try:
    +        minio.init_repo()
    +    except Exception as e:
    +        j.tools.restic.delete(INSTANCE_NAME)
    +        raise j.exceptions.Value(f"Couldn't create restic repo with given data {e}")
    +    else:
    +        minio.save()
    +
    +    return j.data.serializers.json.dumps({"data": "backup repos inited"})
    +
    +
    +
    +def repos_exist(self) ‑> bool +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def repos_exist(self) -> bool:
    +    return INSTANCE_NAME in j.tools.restic.list_all()
    +
    +
    +
    +def restore(self) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def restore(self) -> str:
    +    instance = self._get_instance()
    +    instance.restore("/")
    +    return j.data.serializers.json.dumps({"data": "data restored"})
    +
    +
    +
    +def snapshots(self, tags=None) ‑> str +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def snapshots(self, tags=None) -> str:
    +    if tags:
    +        tags = tags.split(",")
    +    instance = self._get_instance()
    +    result = list(reversed(instance.list_snapshots(tags=tags)))
    +    return j.data.serializers.json.dumps({"data": result})
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/backup/index.html b/docs/api/jumpscale/packages/backup/index.html new file mode 100644 index 000000000..4240f8e7e --- /dev/null +++ b/docs/api/jumpscale/packages/backup/index.html @@ -0,0 +1,76 @@ + + + + + + +jumpscale.packages.backup API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/backup/services/index.html b/docs/api/jumpscale/packages/backup/services/index.html new file mode 100644 index 000000000..859f08d08 --- /dev/null +++ b/docs/api/jumpscale/packages/backup/services/index.html @@ -0,0 +1,66 @@ + + + + + + +jumpscale.packages.backup.services API documentation + + + + + + + + + + + +
    +
    +
    +

    Namespace jumpscale.packages.backup.services

    +
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.packages.backup.services.system_backup
    +
    +

    THE SYSTEM BACKUP SERVICE

    +

    this service will run in the background and execute a backup job for the system paths every hour …

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/backup/services/system_backup.html b/docs/api/jumpscale/packages/backup/services/system_backup.html new file mode 100644 index 000000000..82517cb0e --- /dev/null +++ b/docs/api/jumpscale/packages/backup/services/system_backup.html @@ -0,0 +1,331 @@ + + + + + + +jumpscale.packages.backup.services.system_backup API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.backup.services.system_backup

    +
    +
    +
    +

    THE SYSTEM BACKUP SERVICE

    +
    +

    this service will run in the background and execute a backup job for the system paths every hour

    +

    Examples:

    +

    run the backup service jobs once manually

    +

    JS-NG> j.packages.backup.SystemBackupService().job()

    +

    adding the backup service manually to the servicemanager, although this would done automatically when threebot start

    +
    JS-NG> service_manager = j.tools.servicemanager.new('system_backup_service')
    +JS-NG> service_manager.add_service('system_backup_service', j.sals.fs.expanduser('~/projects/js-sdk//jumpscale/packages/backup/services/system_backup.py'))
    +
    +

    for how to create a backup jobs, check backupjob sal docs

    +
    + +Expand source code + +
    """
    +----------------------------------------------------------------------
    +# THE SYSTEM BACKUP SERVICE
    +----------------------------------------------------------------------
    +this service will run in the background and execute a backup job for the system paths every hour
    +
    +Examples:
    +## run the backup service jobs once manually
    +JS-NG> j.packages.backup.SystemBackupService().job()
    +
    +## adding the backup service manually to the servicemanager, although this would done automatically when threebot start
    +```python
    +JS-NG> service_manager = j.tools.servicemanager.new('system_backup_service')
    +JS-NG> service_manager.add_service('system_backup_service', j.sals.fs.expanduser('~/projects/js-sdk//jumpscale/packages/backup/services/system_backup.py'))
    +```
    +## for how to create a backup jobs, check backupjob sal docs
    +"""
    +
    +from jumpscale.loader import j
    +from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +from jumpscale.tools.notificationsqueue.queue import LEVEL
    +
    +
    +class SystemBackupService(BackgroundService):
    +    # we will use this  pre defined BackupJop if exists, else we will define it using the info from next section
    +    BACKUP_JOB_NAME = "systembackupjob"
    +
    +    # system BackupJob info
    +    ## this ResticRepo instance must be preconfigured and exist.
    +    RESTIC_CLIENT_NAMES = ["systembackupclient"]
    +    ## paths to include in the BackupJob
    +    BACKUP_JOB_PATHS = ["~/.config/jumpscale/", "~/sandbox/cfg/", "~/.ssh/"]
    +    ## paths to exclude. absolute paths will not work as the exclude path should be inside one of the specified backup paths.
    +    PATHS_TO_EXCLUDE = [".config/jumpscale/logs"]
    +
    +    def __init__(self, interval=60 * 60, *args, **kwargs):
    +        super().__init__(interval, *args, **kwargs)
    +
    +    @classmethod
    +    def _create_system_backup_job(cls):
    +        repos_are_ready = all([client in j.tools.restic.list_all() for client in cls.RESTIC_CLIENT_NAMES])
    +        if repos_are_ready:
    +            backupjob = j.sals.backupjob.new(
    +                cls.BACKUP_JOB_NAME,
    +                clients=cls.RESTIC_CLIENT_NAMES,
    +                paths=cls.BACKUP_JOB_PATHS,
    +                paths_to_exclude=cls.PATHS_TO_EXCLUDE,
    +            )
    +            backupjob.save()
    +            return True
    +        else:
    +            return False
    +
    +    def job(self):
    +        """Background backup job to be scheduled.
    +        """
    +        j.logger.info(f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} started.")
    +        if self.BACKUP_JOB_NAME not in j.sals.backupjob.list_all():
    +            j.logger.warning(
    +                f"[Backup Package - System Backup Service] couldn't get instance of BackupJob with name {self.BACKUP_JOB_NAME}!"
    +            )
    +
    +            if not SystemBackupService._create_system_backup_job():
    +                j.logger.error(
    +                    f"[Backup Package - System Backup Service] There is no preconfigure restic repo/s. Backup job won't executed!"
    +                )
    +                return
    +            j.logger.info(
    +                f"[Backup Package - System Backup Service] {self.BACKUP_JOB_NAME} job successfully created\npaths to backup: {self.BACKUP_JOB_PATHS}\npaths excluded: {self.PATHS_TO_EXCLUDE}."
    +            )
    +
    +        backupjob = j.sals.backupjob.get(self.BACKUP_JOB_NAME)
    +        job_completed = backupjob.execute(block=True)
    +        if job_completed:
    +            j.logger.info(
    +                f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} completed successfully."
    +            )
    +            j.tools.notificationsqueue.push(
    +                f"the System backup job completed successfully.", category="SystemBackupService", level=LEVEL.INFO
    +            )
    +        else:
    +            j.logger.error(f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} failed.")
    +            j.tools.notificationsqueue.push(
    +                f"the System backup job failed.", category="SystemBackupService", level=LEVEL.ERROR
    +            )
    +
    +
    +service = SystemBackupService()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SystemBackupService +(interval=3600, *args, **kwargs) +
    +
    +

    Helper class that provides a standard way to create an ABC using +inheritance.

    +

    Abstract base class for background services managed by the service manager

    +

    Arguments

    +

    interval (int | CronTab object | str): scheduled job is executed every interval in seconds / CronTab object / CronTab-formatted string

    +
    + +Expand source code + +
    class SystemBackupService(BackgroundService):
    +    # we will use this  pre defined BackupJop if exists, else we will define it using the info from next section
    +    BACKUP_JOB_NAME = "systembackupjob"
    +
    +    # system BackupJob info
    +    ## this ResticRepo instance must be preconfigured and exist.
    +    RESTIC_CLIENT_NAMES = ["systembackupclient"]
    +    ## paths to include in the BackupJob
    +    BACKUP_JOB_PATHS = ["~/.config/jumpscale/", "~/sandbox/cfg/", "~/.ssh/"]
    +    ## paths to exclude. absolute paths will not work as the exclude path should be inside one of the specified backup paths.
    +    PATHS_TO_EXCLUDE = [".config/jumpscale/logs"]
    +
    +    def __init__(self, interval=60 * 60, *args, **kwargs):
    +        super().__init__(interval, *args, **kwargs)
    +
    +    @classmethod
    +    def _create_system_backup_job(cls):
    +        repos_are_ready = all([client in j.tools.restic.list_all() for client in cls.RESTIC_CLIENT_NAMES])
    +        if repos_are_ready:
    +            backupjob = j.sals.backupjob.new(
    +                cls.BACKUP_JOB_NAME,
    +                clients=cls.RESTIC_CLIENT_NAMES,
    +                paths=cls.BACKUP_JOB_PATHS,
    +                paths_to_exclude=cls.PATHS_TO_EXCLUDE,
    +            )
    +            backupjob.save()
    +            return True
    +        else:
    +            return False
    +
    +    def job(self):
    +        """Background backup job to be scheduled.
    +        """
    +        j.logger.info(f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} started.")
    +        if self.BACKUP_JOB_NAME not in j.sals.backupjob.list_all():
    +            j.logger.warning(
    +                f"[Backup Package - System Backup Service] couldn't get instance of BackupJob with name {self.BACKUP_JOB_NAME}!"
    +            )
    +
    +            if not SystemBackupService._create_system_backup_job():
    +                j.logger.error(
    +                    f"[Backup Package - System Backup Service] There is no preconfigure restic repo/s. Backup job won't executed!"
    +                )
    +                return
    +            j.logger.info(
    +                f"[Backup Package - System Backup Service] {self.BACKUP_JOB_NAME} job successfully created\npaths to backup: {self.BACKUP_JOB_PATHS}\npaths excluded: {self.PATHS_TO_EXCLUDE}."
    +            )
    +
    +        backupjob = j.sals.backupjob.get(self.BACKUP_JOB_NAME)
    +        job_completed = backupjob.execute(block=True)
    +        if job_completed:
    +            j.logger.info(
    +                f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} completed successfully."
    +            )
    +            j.tools.notificationsqueue.push(
    +                f"the System backup job completed successfully.", category="SystemBackupService", level=LEVEL.INFO
    +            )
    +        else:
    +            j.logger.error(f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} failed.")
    +            j.tools.notificationsqueue.push(
    +                f"the System backup job failed.", category="SystemBackupService", level=LEVEL.ERROR
    +            )
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var BACKUP_JOB_NAME
    +
    +
    +
    +
    var BACKUP_JOB_PATHS
    +
    +
    +
    +
    var PATHS_TO_EXCLUDE
    +
    +
    +
    +
    var RESTIC_CLIENT_NAMES
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def job(self) +
    +
    +

    Background backup job to be scheduled.

    +
    + +Expand source code + +
    def job(self):
    +    """Background backup job to be scheduled.
    +    """
    +    j.logger.info(f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} started.")
    +    if self.BACKUP_JOB_NAME not in j.sals.backupjob.list_all():
    +        j.logger.warning(
    +            f"[Backup Package - System Backup Service] couldn't get instance of BackupJob with name {self.BACKUP_JOB_NAME}!"
    +        )
    +
    +        if not SystemBackupService._create_system_backup_job():
    +            j.logger.error(
    +                f"[Backup Package - System Backup Service] There is no preconfigure restic repo/s. Backup job won't executed!"
    +            )
    +            return
    +        j.logger.info(
    +            f"[Backup Package - System Backup Service] {self.BACKUP_JOB_NAME} job successfully created\npaths to backup: {self.BACKUP_JOB_PATHS}\npaths excluded: {self.PATHS_TO_EXCLUDE}."
    +        )
    +
    +    backupjob = j.sals.backupjob.get(self.BACKUP_JOB_NAME)
    +    job_completed = backupjob.execute(block=True)
    +    if job_completed:
    +        j.logger.info(
    +            f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} completed successfully."
    +        )
    +        j.tools.notificationsqueue.push(
    +            f"the System backup job completed successfully.", category="SystemBackupService", level=LEVEL.INFO
    +        )
    +    else:
    +        j.logger.error(f"[Backup Package - System Backup Service] Backup job {self.BACKUP_JOB_NAME} failed.")
    +        j.tools.notificationsqueue.push(
    +            f"the System backup job failed.", category="SystemBackupService", level=LEVEL.ERROR
    +        )
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/billing/index.html b/docs/api/jumpscale/packages/billing/index.html new file mode 100644 index 000000000..a446c1bd3 --- /dev/null +++ b/docs/api/jumpscale/packages/billing/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.billing API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/billing/services/billing.html b/docs/api/jumpscale/packages/billing/services/billing.html new file mode 100644 index 000000000..f8d2285b0 --- /dev/null +++ b/docs/api/jumpscale/packages/billing/services/billing.html @@ -0,0 +1,125 @@ + + + + + + +jumpscale.packages.billing.services.billing API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.billing.services.billing

    +
    +
    +
    + +Expand source code + +
    from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +from jumpscale.loader import j
    +import gevent
    +
    +
    +class BillingService(BackgroundService):
    +    def __init__(self, interval=1, *args, **kwargs):
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def job(self):
    +        j.sals.billing.process_payments()
    +        j.sals.billing.refund_extra()
    +        j.sals.billing.refund_failed_payments()
    +        j.sals.billing.process_refunds()
    +        gevent.sleep(5)
    +
    +
    +service = BillingService()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class BillingService +(interval=1, *args, **kwargs) +
    +
    +

    Helper class that provides a standard way to create an ABC using +inheritance.

    +

    Abstract base class for background services managed by the service manager

    +

    Arguments

    +

    interval (int | CronTab object | str): scheduled job is executed every interval in seconds / CronTab object / CronTab-formatted string

    +
    + +Expand source code + +
    class BillingService(BackgroundService):
    +    def __init__(self, interval=1, *args, **kwargs):
    +        super().__init__(interval, *args, **kwargs)
    +
    +    def job(self):
    +        j.sals.billing.process_payments()
    +        j.sals.billing.refund_extra()
    +        j.sals.billing.refund_failed_payments()
    +        j.sals.billing.process_refunds()
    +        gevent.sleep(5)
    +
    +

    Ancestors

    + +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/billing/services/index.html b/docs/api/jumpscale/packages/billing/services/index.html new file mode 100644 index 000000000..b04fc1a2b --- /dev/null +++ b/docs/api/jumpscale/packages/billing/services/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.billing.services API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/chatflows/actors/chatbot.html b/docs/api/jumpscale/packages/chatflows/actors/chatbot.html new file mode 100644 index 000000000..8f95c084a --- /dev/null +++ b/docs/api/jumpscale/packages/chatflows/actors/chatbot.html @@ -0,0 +1,703 @@ + + + + + + +jumpscale.packages.chatflows.actors.chatbot API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.chatflows.actors.chatbot

    +
    +
    +
    + +Expand source code + +
    from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
    +from jumpscale.loader import j
    +import base64
    +import sys
    +import uuid
    +import importlib
    +import os
    +
    +
    +class ChatFlows(BaseActor):
    +    def __init__(self):
    +        super().__init__()
    +        self.chats = {}
    +        self.sessions = {}
    +
    +    @actor_method
    +    def new(self, package: str, chat: str, client_ip: str, query_params: dict = None) -> dict:
    +        package_object = self.chats.get(package)
    +        if not package_object:
    +            raise j.exceptions.Value(f"Package {package} not found")
    +
    +        chatflow = package_object.get(chat)
    +        if not chatflow:
    +            raise j.exceptions.Value(f"Chat {chat} not found")
    +
    +        if query_params is None:
    +            query_params = {}
    +        obj = chatflow(**query_params)
    +        self.sessions[obj.session_id] = obj
    +        return {"sessionId": obj.session_id, "title": obj.title}
    +
    +    @actor_method
    +    def fetch(self, session_id: str, restore: bool = False) -> dict:
    +        chatflow = self.sessions.get(session_id)
    +        if not chatflow:
    +            return {"payload": {"category": "end"}}
    +
    +        result = chatflow.get_work(restore)
    +
    +        if result.get("category") == "end":
    +            self.sessions.pop(session_id)
    +
    +        return result
    +
    +    @actor_method
    +    def validate(self, session_id: str) -> dict:
    +        return {"valid": session_id in self.sessions}
    +
    +    @actor_method
    +    def report(self, session_id: str, result: str = None):
    +        chatflow = self.sessions.get(session_id)
    +        chatflow.set_work(result)
    +
    +    @actor_method
    +    def back(self, session_id: str):
    +        chatflow = self.sessions.get(session_id)
    +        chatflow.go_back()
    +
    +    @actor_method
    +    def chatflows_list(self) -> list:
    +        return list(self.chats.keys())
    +
    +    def _scan_chats(self, path):
    +        package = j.sals.fs.basename(j.sals.fs.parent(path))
    +        for path in j.sals.fs.walk_files(path, recursive=False):
    +            if path.endswith(".py"):
    +                name = j.sals.fs.stem(path)
    +                yield path, package, name
    +
    +    def _import_path(self, filepath):
    +        absolute_path = os.path.abspath(filepath)
    +        paths = sys.path[:]
    +        paths.sort(key=lambda p: len(p), reverse=True)
    +        for syspath in paths:
    +            absolute_sys_path = os.path.abspath(syspath)
    +            if absolute_path.startswith(absolute_sys_path):
    +                parts = absolute_path[len(absolute_sys_path) + 1 : -3].split(os.path.sep)
    +                if "__init__" in parts:
    +                    parts.remove("__init__")
    +                module_name = ".".join(parts)
    +                break
    +        else:
    +            module_name = absolute_path
    +
    +        spec = importlib.util.spec_from_file_location(module_name, absolute_path)
    +        module = importlib.util.module_from_spec(spec)
    +        sys.modules[module_name] = module
    +        spec.loader.exec_module(module)
    +        return module
    +
    +    @actor_method
    +    def load(self, path: str):
    +        for path, package, chat in self._scan_chats(path):
    +            module = self._import_path(path)
    +            if package not in self.chats:
    +                self.chats[package] = {}
    +
    +            self.chats[package][chat] = module.chat
    +
    +    @actor_method
    +    def unload(self, path: str):
    +        for _, package, chat in self._scan_chats(path):
    +            self.chats[package].pop(chat)
    +
    +
    +Actor = ChatFlows
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class ChatFlows +
    +
    +
    +
    + +Expand source code + +
    class ChatFlows(BaseActor):
    +    def __init__(self):
    +        super().__init__()
    +        self.chats = {}
    +        self.sessions = {}
    +
    +    @actor_method
    +    def new(self, package: str, chat: str, client_ip: str, query_params: dict = None) -> dict:
    +        package_object = self.chats.get(package)
    +        if not package_object:
    +            raise j.exceptions.Value(f"Package {package} not found")
    +
    +        chatflow = package_object.get(chat)
    +        if not chatflow:
    +            raise j.exceptions.Value(f"Chat {chat} not found")
    +
    +        if query_params is None:
    +            query_params = {}
    +        obj = chatflow(**query_params)
    +        self.sessions[obj.session_id] = obj
    +        return {"sessionId": obj.session_id, "title": obj.title}
    +
    +    @actor_method
    +    def fetch(self, session_id: str, restore: bool = False) -> dict:
    +        chatflow = self.sessions.get(session_id)
    +        if not chatflow:
    +            return {"payload": {"category": "end"}}
    +
    +        result = chatflow.get_work(restore)
    +
    +        if result.get("category") == "end":
    +            self.sessions.pop(session_id)
    +
    +        return result
    +
    +    @actor_method
    +    def validate(self, session_id: str) -> dict:
    +        return {"valid": session_id in self.sessions}
    +
    +    @actor_method
    +    def report(self, session_id: str, result: str = None):
    +        chatflow = self.sessions.get(session_id)
    +        chatflow.set_work(result)
    +
    +    @actor_method
    +    def back(self, session_id: str):
    +        chatflow = self.sessions.get(session_id)
    +        chatflow.go_back()
    +
    +    @actor_method
    +    def chatflows_list(self) -> list:
    +        return list(self.chats.keys())
    +
    +    def _scan_chats(self, path):
    +        package = j.sals.fs.basename(j.sals.fs.parent(path))
    +        for path in j.sals.fs.walk_files(path, recursive=False):
    +            if path.endswith(".py"):
    +                name = j.sals.fs.stem(path)
    +                yield path, package, name
    +
    +    def _import_path(self, filepath):
    +        absolute_path = os.path.abspath(filepath)
    +        paths = sys.path[:]
    +        paths.sort(key=lambda p: len(p), reverse=True)
    +        for syspath in paths:
    +            absolute_sys_path = os.path.abspath(syspath)
    +            if absolute_path.startswith(absolute_sys_path):
    +                parts = absolute_path[len(absolute_sys_path) + 1 : -3].split(os.path.sep)
    +                if "__init__" in parts:
    +                    parts.remove("__init__")
    +                module_name = ".".join(parts)
    +                break
    +        else:
    +            module_name = absolute_path
    +
    +        spec = importlib.util.spec_from_file_location(module_name, absolute_path)
    +        module = importlib.util.module_from_spec(spec)
    +        sys.modules[module_name] = module
    +        spec.loader.exec_module(module)
    +        return module
    +
    +    @actor_method
    +    def load(self, path: str):
    +        for path, package, chat in self._scan_chats(path):
    +            module = self._import_path(path)
    +            if package not in self.chats:
    +                self.chats[package] = {}
    +
    +            self.chats[package][chat] = module.chat
    +
    +    @actor_method
    +    def unload(self, path: str):
    +        for _, package, chat in self._scan_chats(path):
    +            self.chats[package].pop(chat)
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def back(self, session_id: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def back(self, session_id: str):
    +    chatflow = self.sessions.get(session_id)
    +    chatflow.go_back()
    +
    +
    +
    +def chatflows_list(self) ‑> list +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def chatflows_list(self) -> list:
    +    return list(self.chats.keys())
    +
    +
    +
    +def fetch(self, session_id: str, restore: bool = False) ‑> dict +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def fetch(self, session_id: str, restore: bool = False) -> dict:
    +    chatflow = self.sessions.get(session_id)
    +    if not chatflow:
    +        return {"payload": {"category": "end"}}
    +
    +    result = chatflow.get_work(restore)
    +
    +    if result.get("category") == "end":
    +        self.sessions.pop(session_id)
    +
    +    return result
    +
    +
    +
    +def load(self, path: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def load(self, path: str):
    +    for path, package, chat in self._scan_chats(path):
    +        module = self._import_path(path)
    +        if package not in self.chats:
    +            self.chats[package] = {}
    +
    +        self.chats[package][chat] = module.chat
    +
    +
    +
    +def new(self, package: str, chat: str, client_ip: str, query_params: dict = None) ‑> dict +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def new(self, package: str, chat: str, client_ip: str, query_params: dict = None) -> dict:
    +    package_object = self.chats.get(package)
    +    if not package_object:
    +        raise j.exceptions.Value(f"Package {package} not found")
    +
    +    chatflow = package_object.get(chat)
    +    if not chatflow:
    +        raise j.exceptions.Value(f"Chat {chat} not found")
    +
    +    if query_params is None:
    +        query_params = {}
    +    obj = chatflow(**query_params)
    +    self.sessions[obj.session_id] = obj
    +    return {"sessionId": obj.session_id, "title": obj.title}
    +
    +
    +
    +def report(self, session_id: str, result: str = None) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def report(self, session_id: str, result: str = None):
    +    chatflow = self.sessions.get(session_id)
    +    chatflow.set_work(result)
    +
    +
    +
    +def unload(self, path: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def unload(self, path: str):
    +    for _, package, chat in self._scan_chats(path):
    +        self.chats[package].pop(chat)
    +
    +
    +
    +def validate(self, session_id: str) ‑> dict +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def validate(self, session_id: str) -> dict:
    +    return {"valid": session_id in self.sessions}
    +
    +
    +
    +
    +
    +class Actor +
    +
    +
    +
    + +Expand source code + +
    class ChatFlows(BaseActor):
    +    def __init__(self):
    +        super().__init__()
    +        self.chats = {}
    +        self.sessions = {}
    +
    +    @actor_method
    +    def new(self, package: str, chat: str, client_ip: str, query_params: dict = None) -> dict:
    +        package_object = self.chats.get(package)
    +        if not package_object:
    +            raise j.exceptions.Value(f"Package {package} not found")
    +
    +        chatflow = package_object.get(chat)
    +        if not chatflow:
    +            raise j.exceptions.Value(f"Chat {chat} not found")
    +
    +        if query_params is None:
    +            query_params = {}
    +        obj = chatflow(**query_params)
    +        self.sessions[obj.session_id] = obj
    +        return {"sessionId": obj.session_id, "title": obj.title}
    +
    +    @actor_method
    +    def fetch(self, session_id: str, restore: bool = False) -> dict:
    +        chatflow = self.sessions.get(session_id)
    +        if not chatflow:
    +            return {"payload": {"category": "end"}}
    +
    +        result = chatflow.get_work(restore)
    +
    +        if result.get("category") == "end":
    +            self.sessions.pop(session_id)
    +
    +        return result
    +
    +    @actor_method
    +    def validate(self, session_id: str) -> dict:
    +        return {"valid": session_id in self.sessions}
    +
    +    @actor_method
    +    def report(self, session_id: str, result: str = None):
    +        chatflow = self.sessions.get(session_id)
    +        chatflow.set_work(result)
    +
    +    @actor_method
    +    def back(self, session_id: str):
    +        chatflow = self.sessions.get(session_id)
    +        chatflow.go_back()
    +
    +    @actor_method
    +    def chatflows_list(self) -> list:
    +        return list(self.chats.keys())
    +
    +    def _scan_chats(self, path):
    +        package = j.sals.fs.basename(j.sals.fs.parent(path))
    +        for path in j.sals.fs.walk_files(path, recursive=False):
    +            if path.endswith(".py"):
    +                name = j.sals.fs.stem(path)
    +                yield path, package, name
    +
    +    def _import_path(self, filepath):
    +        absolute_path = os.path.abspath(filepath)
    +        paths = sys.path[:]
    +        paths.sort(key=lambda p: len(p), reverse=True)
    +        for syspath in paths:
    +            absolute_sys_path = os.path.abspath(syspath)
    +            if absolute_path.startswith(absolute_sys_path):
    +                parts = absolute_path[len(absolute_sys_path) + 1 : -3].split(os.path.sep)
    +                if "__init__" in parts:
    +                    parts.remove("__init__")
    +                module_name = ".".join(parts)
    +                break
    +        else:
    +            module_name = absolute_path
    +
    +        spec = importlib.util.spec_from_file_location(module_name, absolute_path)
    +        module = importlib.util.module_from_spec(spec)
    +        sys.modules[module_name] = module
    +        spec.loader.exec_module(module)
    +        return module
    +
    +    @actor_method
    +    def load(self, path: str):
    +        for path, package, chat in self._scan_chats(path):
    +            module = self._import_path(path)
    +            if package not in self.chats:
    +                self.chats[package] = {}
    +
    +            self.chats[package][chat] = module.chat
    +
    +    @actor_method
    +    def unload(self, path: str):
    +        for _, package, chat in self._scan_chats(path):
    +            self.chats[package].pop(chat)
    +
    +

    Ancestors

    + +

    Methods

    +
    +
    +def back(self, session_id: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def back(self, session_id: str):
    +    chatflow = self.sessions.get(session_id)
    +    chatflow.go_back()
    +
    +
    +
    +def chatflows_list(self) ‑> list +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def chatflows_list(self) -> list:
    +    return list(self.chats.keys())
    +
    +
    +
    +def fetch(self, session_id: str, restore: bool = False) ‑> dict +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def fetch(self, session_id: str, restore: bool = False) -> dict:
    +    chatflow = self.sessions.get(session_id)
    +    if not chatflow:
    +        return {"payload": {"category": "end"}}
    +
    +    result = chatflow.get_work(restore)
    +
    +    if result.get("category") == "end":
    +        self.sessions.pop(session_id)
    +
    +    return result
    +
    +
    +
    +def load(self, path: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def load(self, path: str):
    +    for path, package, chat in self._scan_chats(path):
    +        module = self._import_path(path)
    +        if package not in self.chats:
    +            self.chats[package] = {}
    +
    +        self.chats[package][chat] = module.chat
    +
    +
    +
    +def new(self, package: str, chat: str, client_ip: str, query_params: dict = None) ‑> dict +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def new(self, package: str, chat: str, client_ip: str, query_params: dict = None) -> dict:
    +    package_object = self.chats.get(package)
    +    if not package_object:
    +        raise j.exceptions.Value(f"Package {package} not found")
    +
    +    chatflow = package_object.get(chat)
    +    if not chatflow:
    +        raise j.exceptions.Value(f"Chat {chat} not found")
    +
    +    if query_params is None:
    +        query_params = {}
    +    obj = chatflow(**query_params)
    +    self.sessions[obj.session_id] = obj
    +    return {"sessionId": obj.session_id, "title": obj.title}
    +
    +
    +
    +def report(self, session_id: str, result: str = None) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def report(self, session_id: str, result: str = None):
    +    chatflow = self.sessions.get(session_id)
    +    chatflow.set_work(result)
    +
    +
    +
    +def unload(self, path: str) +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def unload(self, path: str):
    +    for _, package, chat in self._scan_chats(path):
    +        self.chats[package].pop(chat)
    +
    +
    +
    +def validate(self, session_id: str) ‑> dict +
    +
    +
    +
    + +Expand source code + +
    @actor_method
    +def validate(self, session_id: str) -> dict:
    +    return {"valid": session_id in self.sessions}
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/chatflows/actors/index.html b/docs/api/jumpscale/packages/chatflows/actors/index.html new file mode 100644 index 000000000..0e32204dd --- /dev/null +++ b/docs/api/jumpscale/packages/chatflows/actors/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.chatflows.actors API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/chatflows/bottle/bottle.html b/docs/api/jumpscale/packages/chatflows/bottle/bottle.html new file mode 100644 index 000000000..eb1659de0 --- /dev/null +++ b/docs/api/jumpscale/packages/chatflows/bottle/bottle.html @@ -0,0 +1,134 @@ + + + + + + +jumpscale.packages.chatflows.bottle.bottle API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.chatflows.bottle.bottle

    +
    +
    +
    + +Expand source code + +
    from bottle import Bottle, abort, request
    +
    +from jumpscale.loader import j
    +from jumpscale.packages.auth.bottle.auth import login_required
    +
    +app = Bottle()
    +
    +templates_path = j.sals.fs.join_paths(j.sals.fs.dirname(__file__), "..", "frontend")
    +env = j.tools.jinja2.get_env(templates_path)
    +
    +
    +@app.route("/<package_name>")
    +@login_required
    +def chats(package_name):
    +    threebot = j.servers.threebot.get()
    +    package = threebot.packages.get(package_name)
    +    if not package:
    +        abort(404, f"package {package_name} does not exist")
    +    chatflows = package.get_chats()
    +    return j.data.serializers.json.dumps(list(chatflows.keys()))
    +
    +
    +@app.route("/<package_name>/chats/<chat_name>")
    +@login_required
    +def chat(package_name, chat_name):
    +    session = request.environ.get("beaker.session", {})
    +    return env.get_template("index.html").render(
    +        package=package_name, chat=chat_name, username=session.get("username", ""), email=session.get("email", "")
    +    )
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def chat(package_name, chat_name) +
    +
    +
    +
    + +Expand source code + +
    @app.route("/<package_name>/chats/<chat_name>")
    +@login_required
    +def chat(package_name, chat_name):
    +    session = request.environ.get("beaker.session", {})
    +    return env.get_template("index.html").render(
    +        package=package_name, chat=chat_name, username=session.get("username", ""), email=session.get("email", "")
    +    )
    +
    +
    +
    +def chats(package_name) +
    +
    +
    +
    + +Expand source code + +
    @app.route("/<package_name>")
    +@login_required
    +def chats(package_name):
    +    threebot = j.servers.threebot.get()
    +    package = threebot.packages.get(package_name)
    +    if not package:
    +        abort(404, f"package {package_name} does not exist")
    +    chatflows = package.get_chats()
    +    return j.data.serializers.json.dumps(list(chatflows.keys()))
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/chatflows/bottle/index.html b/docs/api/jumpscale/packages/chatflows/bottle/index.html new file mode 100644 index 000000000..d9344c757 --- /dev/null +++ b/docs/api/jumpscale/packages/chatflows/bottle/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.chatflows.bottle API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/chatflows/index.html b/docs/api/jumpscale/packages/chatflows/index.html new file mode 100644 index 000000000..6ba8639d0 --- /dev/null +++ b/docs/api/jumpscale/packages/chatflows/index.html @@ -0,0 +1,70 @@ + + + + + + +jumpscale.packages.chatflows API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/codeserver/index.html b/docs/api/jumpscale/packages/codeserver/index.html new file mode 100644 index 000000000..65c04f86a --- /dev/null +++ b/docs/api/jumpscale/packages/codeserver/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.codeserver API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/codeserver/package.html b/docs/api/jumpscale/packages/codeserver/package.html new file mode 100644 index 000000000..52476ecf1 --- /dev/null +++ b/docs/api/jumpscale/packages/codeserver/package.html @@ -0,0 +1,271 @@ + + + + + + +jumpscale.packages.codeserver.package API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.codeserver.package

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +import os
    +
    +
    +class codeserver:
    +    def __init__(self):
    +        self.bin_path = j.sals.fs.join_paths(j.core.dirs.BINDIR, "code-server")
    +        self.script_path = j.sals.fs.join_paths(os.path.dirname(__file__), "codeserver_install.sh")
    +        self._started = False
    +
    +    @property
    +    def started(self):
    +        return self._started
    +
    +    @property
    +    def startupcmd(self):
    +        cmd = j.tools.startupcmd.get("codeserver")
    +        start_cmd = f"{self.bin_path} --auth none --host 127.0.0.1 -p 8080"
    +        cmd.start_cmd = start_cmd
    +        return cmd
    +
    +    def install(self):
    +        """Called when package is added
    +        """
    +        if not j.sals.fs.exists(self.bin_path):
    +            rc, out, err = j.sals.process.execute(f"chmod +x {self.script_path}; {self.script_path}")
    +            if rc:
    +                raise j.exceptions.Runtime(err)
    +
    +    def uninstall(self):
    +        """Called when package is deleted
    +        """
    +        pass
    +
    +    def start(self):
    +        """Called when threebot is started
    +        """
    +        if not self.started:
    +            self.startupcmd.start()
    +            self._started = True
    +
    +    def stop(self):
    +        if self.started:
    +            self.startupcmd.stop()
    +            self._started = False
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class codeserver +
    +
    +
    +
    + +Expand source code + +
    class codeserver:
    +    def __init__(self):
    +        self.bin_path = j.sals.fs.join_paths(j.core.dirs.BINDIR, "code-server")
    +        self.script_path = j.sals.fs.join_paths(os.path.dirname(__file__), "codeserver_install.sh")
    +        self._started = False
    +
    +    @property
    +    def started(self):
    +        return self._started
    +
    +    @property
    +    def startupcmd(self):
    +        cmd = j.tools.startupcmd.get("codeserver")
    +        start_cmd = f"{self.bin_path} --auth none --host 127.0.0.1 -p 8080"
    +        cmd.start_cmd = start_cmd
    +        return cmd
    +
    +    def install(self):
    +        """Called when package is added
    +        """
    +        if not j.sals.fs.exists(self.bin_path):
    +            rc, out, err = j.sals.process.execute(f"chmod +x {self.script_path}; {self.script_path}")
    +            if rc:
    +                raise j.exceptions.Runtime(err)
    +
    +    def uninstall(self):
    +        """Called when package is deleted
    +        """
    +        pass
    +
    +    def start(self):
    +        """Called when threebot is started
    +        """
    +        if not self.started:
    +            self.startupcmd.start()
    +            self._started = True
    +
    +    def stop(self):
    +        if self.started:
    +            self.startupcmd.stop()
    +            self._started = False
    +
    +

    Instance variables

    +
    +
    var started
    +
    +
    +
    + +Expand source code + +
    @property
    +def started(self):
    +    return self._started
    +
    +
    +
    var startupcmd
    +
    +
    +
    + +Expand source code + +
    @property
    +def startupcmd(self):
    +    cmd = j.tools.startupcmd.get("codeserver")
    +    start_cmd = f"{self.bin_path} --auth none --host 127.0.0.1 -p 8080"
    +    cmd.start_cmd = start_cmd
    +    return cmd
    +
    +
    +
    +

    Methods

    +
    +
    +def install(self) +
    +
    +

    Called when package is added

    +
    + +Expand source code + +
    def install(self):
    +    """Called when package is added
    +    """
    +    if not j.sals.fs.exists(self.bin_path):
    +        rc, out, err = j.sals.process.execute(f"chmod +x {self.script_path}; {self.script_path}")
    +        if rc:
    +            raise j.exceptions.Runtime(err)
    +
    +
    +
    +def start(self) +
    +
    +

    Called when threebot is started

    +
    + +Expand source code + +
    def start(self):
    +    """Called when threebot is started
    +    """
    +    if not self.started:
    +        self.startupcmd.start()
    +        self._started = True
    +
    +
    +
    +def stop(self) +
    +
    +
    +
    + +Expand source code + +
    def stop(self):
    +    if self.started:
    +        self.startupcmd.stop()
    +        self._started = False
    +
    +
    +
    +def uninstall(self) +
    +
    +

    Called when package is deleted

    +
    + +Expand source code + +
    def uninstall(self):
    +    """Called when package is deleted
    +    """
    +    pass
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/index.html b/docs/api/jumpscale/packages/index.html new file mode 100644 index 000000000..79a850d5c --- /dev/null +++ b/docs/api/jumpscale/packages/index.html @@ -0,0 +1,110 @@ + + + + + + +jumpscale.packages API documentation + + + + + + + + + + + +
    +
    +
    +

    Namespace jumpscale.packages

    +
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.packages.admin
    +
    +

    Admin package

    +
    +
    jumpscale.packages.auth
    +
    +

    Auth package is responsible for making authentication and authorization using ThreeFold Connect …

    +
    +
    jumpscale.packages.backup
    +
    +
    +
    +
    jumpscale.packages.billing
    +
    +
    +
    +
    jumpscale.packages.chatflows
    +
    +
    +
    +
    jumpscale.packages.codeserver
    +
    +
    +
    +
    jumpscale.packages.notebooks
    +
    +
    +
    +
    jumpscale.packages.polls
    +
    +

    Polls Package …

    +
    +
    jumpscale.packages.stellar_stats
    +
    +
    +
    +
    jumpscale.packages.weblibs
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/notebooks/index.html b/docs/api/jumpscale/packages/notebooks/index.html new file mode 100644 index 000000000..077a0a803 --- /dev/null +++ b/docs/api/jumpscale/packages/notebooks/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.notebooks API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/notebooks/package.html b/docs/api/jumpscale/packages/notebooks/package.html new file mode 100644 index 000000000..200ad6c5f --- /dev/null +++ b/docs/api/jumpscale/packages/notebooks/package.html @@ -0,0 +1,386 @@ + + + + + + +jumpscale.packages.notebooks.package API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.notebooks.package

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +
    +PYTHON_PACKAGES = [
    +    "jupyterlab",
    +    "voila",
    +    "voila-gridstack",
    +    "voila-vuetify",
    +    "matplotlib",
    +    "ipywidgets",
    +    "jupyterlab_code_formatter",
    +]
    +
    +
    +class notebooks:
    +    def __init__(self):
    +        self.notebook_dir = j.sals.fs.join_paths(j.core.dirs.BASEDIR)
    +
    +    def get_cmd(self, voila=False, base_url=None, ip="127.0.0.1", port=8888):
    +        if not voila:
    +            # This needs to be executed in the same process and startup cmds uses exec -a <process-name>
    +            # so if we used a semicolon, it will seperate the execution
    +            cmd = "jupyter serverextension enable --py jupyterlab_code_formatter\n"
    +            cmd += "jupyter lab --no-browser --NotebookApp.allow_remote_access=True --NotebookApp.token=''"
    +            cmd += f" --NotebookApp.password='' --ip={ip} --port={port} --allow-root"
    +        else:
    +            cmd = f"voila --Voila.ip={ip}  --Voila.port={port}"
    +
    +        if base_url:
    +            cmd += f" --NotebookApp.base_url={base_url}"
    +        return cmd
    +
    +    @property
    +    def startupcmd(self):
    +        cmd = j.tools.startupcmd.get("notebooks")
    +        start_cmd = self.get_cmd(base_url="/notebooks/")
    +        cmd.start_cmd = start_cmd
    +        cmd.ports = [8888]
    +        cmd.save()
    +        return cmd
    +
    +    def install(self):
    +        """Called when package is added
    +        """
    +        rc, _, _ = j.sals.process.execute("python -c 'import jupyterlab'")
    +        if rc:
    +            for package in PYTHON_PACKAGES:
    +                j.logger.info(f"Installing {package}...")
    +                rc, _, err = j.sals.process.execute(f"pip3 install {package}")
    +                if rc:
    +                    raise j.exceptions.Runtime(err)
    +
    +            cmd = """
    +            jupyter labextension install @jupyter-voila/jupyterlab-preview --no-build
    +            jupyter labextension install @ryantam626/jupyterlab_code_formatter --no-build
    +            jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build
    +            jupyter labextension install voila --no-build
    +
    +            jupyter lab build  --minimize=False
    +
    +            jupyter extension enable voila --sys-prefix
    +            jupyter nbextension install voila --sys-prefix --py
    +            jupyter nbextension enable voila --sys-prefix --py
    +
    +            """
    +            j.logger.info("Installing jupyter labextensions...")
    +            rc, _, err = j.sals.process.execute(cmd, showout=True)
    +            if rc:
    +                raise j.exceptions.Runtime(err)
    +
    +    def uninstall(self):
    +        """Called when package is deleted
    +        """
    +        rc, _, _ = j.sals.process.execute("python -c 'import jupyterlab'")
    +        if not rc:
    +            for package in PYTHON_PACKAGES:
    +                rc, _, err = j.sals.process.execute(f"pip3 uninstall -y {package}", showout=True)
    +                if rc:
    +                    raise j.exceptions.Runtime(err)
    +
    +    def start(self):
    +        """Called when threebot is started
    +        """
    +        if not self.startupcmd.is_running():
    +            self.startupcmd.start()
    +
    +    def stop(self):
    +        if self.startupcmd.is_running():
    +            self.startupcmd.stop()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class notebooks +
    +
    +
    +
    + +Expand source code + +
    class notebooks:
    +    def __init__(self):
    +        self.notebook_dir = j.sals.fs.join_paths(j.core.dirs.BASEDIR)
    +
    +    def get_cmd(self, voila=False, base_url=None, ip="127.0.0.1", port=8888):
    +        if not voila:
    +            # This needs to be executed in the same process and startup cmds uses exec -a <process-name>
    +            # so if we used a semicolon, it will seperate the execution
    +            cmd = "jupyter serverextension enable --py jupyterlab_code_formatter\n"
    +            cmd += "jupyter lab --no-browser --NotebookApp.allow_remote_access=True --NotebookApp.token=''"
    +            cmd += f" --NotebookApp.password='' --ip={ip} --port={port} --allow-root"
    +        else:
    +            cmd = f"voila --Voila.ip={ip}  --Voila.port={port}"
    +
    +        if base_url:
    +            cmd += f" --NotebookApp.base_url={base_url}"
    +        return cmd
    +
    +    @property
    +    def startupcmd(self):
    +        cmd = j.tools.startupcmd.get("notebooks")
    +        start_cmd = self.get_cmd(base_url="/notebooks/")
    +        cmd.start_cmd = start_cmd
    +        cmd.ports = [8888]
    +        cmd.save()
    +        return cmd
    +
    +    def install(self):
    +        """Called when package is added
    +        """
    +        rc, _, _ = j.sals.process.execute("python -c 'import jupyterlab'")
    +        if rc:
    +            for package in PYTHON_PACKAGES:
    +                j.logger.info(f"Installing {package}...")
    +                rc, _, err = j.sals.process.execute(f"pip3 install {package}")
    +                if rc:
    +                    raise j.exceptions.Runtime(err)
    +
    +            cmd = """
    +            jupyter labextension install @jupyter-voila/jupyterlab-preview --no-build
    +            jupyter labextension install @ryantam626/jupyterlab_code_formatter --no-build
    +            jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build
    +            jupyter labextension install voila --no-build
    +
    +            jupyter lab build  --minimize=False
    +
    +            jupyter extension enable voila --sys-prefix
    +            jupyter nbextension install voila --sys-prefix --py
    +            jupyter nbextension enable voila --sys-prefix --py
    +
    +            """
    +            j.logger.info("Installing jupyter labextensions...")
    +            rc, _, err = j.sals.process.execute(cmd, showout=True)
    +            if rc:
    +                raise j.exceptions.Runtime(err)
    +
    +    def uninstall(self):
    +        """Called when package is deleted
    +        """
    +        rc, _, _ = j.sals.process.execute("python -c 'import jupyterlab'")
    +        if not rc:
    +            for package in PYTHON_PACKAGES:
    +                rc, _, err = j.sals.process.execute(f"pip3 uninstall -y {package}", showout=True)
    +                if rc:
    +                    raise j.exceptions.Runtime(err)
    +
    +    def start(self):
    +        """Called when threebot is started
    +        """
    +        if not self.startupcmd.is_running():
    +            self.startupcmd.start()
    +
    +    def stop(self):
    +        if self.startupcmd.is_running():
    +            self.startupcmd.stop()
    +
    +

    Instance variables

    +
    +
    var startupcmd
    +
    +
    +
    + +Expand source code + +
    @property
    +def startupcmd(self):
    +    cmd = j.tools.startupcmd.get("notebooks")
    +    start_cmd = self.get_cmd(base_url="/notebooks/")
    +    cmd.start_cmd = start_cmd
    +    cmd.ports = [8888]
    +    cmd.save()
    +    return cmd
    +
    +
    +
    +

    Methods

    +
    +
    +def get_cmd(self, voila=False, base_url=None, ip='127.0.0.1', port=8888) +
    +
    +
    +
    + +Expand source code + +
    def get_cmd(self, voila=False, base_url=None, ip="127.0.0.1", port=8888):
    +    if not voila:
    +        # This needs to be executed in the same process and startup cmds uses exec -a <process-name>
    +        # so if we used a semicolon, it will seperate the execution
    +        cmd = "jupyter serverextension enable --py jupyterlab_code_formatter\n"
    +        cmd += "jupyter lab --no-browser --NotebookApp.allow_remote_access=True --NotebookApp.token=''"
    +        cmd += f" --NotebookApp.password='' --ip={ip} --port={port} --allow-root"
    +    else:
    +        cmd = f"voila --Voila.ip={ip}  --Voila.port={port}"
    +
    +    if base_url:
    +        cmd += f" --NotebookApp.base_url={base_url}"
    +    return cmd
    +
    +
    +
    +def install(self) +
    +
    +

    Called when package is added

    +
    + +Expand source code + +
    def install(self):
    +    """Called when package is added
    +    """
    +    rc, _, _ = j.sals.process.execute("python -c 'import jupyterlab'")
    +    if rc:
    +        for package in PYTHON_PACKAGES:
    +            j.logger.info(f"Installing {package}...")
    +            rc, _, err = j.sals.process.execute(f"pip3 install {package}")
    +            if rc:
    +                raise j.exceptions.Runtime(err)
    +
    +        cmd = """
    +        jupyter labextension install @jupyter-voila/jupyterlab-preview --no-build
    +        jupyter labextension install @ryantam626/jupyterlab_code_formatter --no-build
    +        jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build
    +        jupyter labextension install voila --no-build
    +
    +        jupyter lab build  --minimize=False
    +
    +        jupyter extension enable voila --sys-prefix
    +        jupyter nbextension install voila --sys-prefix --py
    +        jupyter nbextension enable voila --sys-prefix --py
    +
    +        """
    +        j.logger.info("Installing jupyter labextensions...")
    +        rc, _, err = j.sals.process.execute(cmd, showout=True)
    +        if rc:
    +            raise j.exceptions.Runtime(err)
    +
    +
    +
    +def start(self) +
    +
    +

    Called when threebot is started

    +
    + +Expand source code + +
    def start(self):
    +    """Called when threebot is started
    +    """
    +    if not self.startupcmd.is_running():
    +        self.startupcmd.start()
    +
    +
    +
    +def stop(self) +
    +
    +
    +
    + +Expand source code + +
    def stop(self):
    +    if self.startupcmd.is_running():
    +        self.startupcmd.stop()
    +
    +
    +
    +def uninstall(self) +
    +
    +

    Called when package is deleted

    +
    + +Expand source code + +
    def uninstall(self):
    +    """Called when package is deleted
    +    """
    +    rc, _, _ = j.sals.process.execute("python -c 'import jupyterlab'")
    +    if not rc:
    +        for package in PYTHON_PACKAGES:
    +            rc, _, err = j.sals.process.execute(f"pip3 uninstall -y {package}", showout=True)
    +            if rc:
    +                raise j.exceptions.Runtime(err)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/bottle/index.html b/docs/api/jumpscale/packages/polls/bottle/index.html new file mode 100644 index 000000000..6e681ba00 --- /dev/null +++ b/docs/api/jumpscale/packages/polls/bottle/index.html @@ -0,0 +1,83 @@ + + + + + + +jumpscale.packages.polls.bottle API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.polls.bottle

    +
    +
    +

    Bottle server for tf council

    +

    End points

    + +
    + +Expand source code + +
    """Bottle server for tf council
    +
    +End points
    +
    +- http://localhost/polls/api/names : print list of voters names
    +- http://localhost/polls/api/results : print the votes results
    +"""
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.packages.polls.bottle.polls_bottle
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/bottle/polls_bottle.html b/docs/api/jumpscale/packages/polls/bottle/polls_bottle.html new file mode 100644 index 000000000..07a62a45b --- /dev/null +++ b/docs/api/jumpscale/packages/polls/bottle/polls_bottle.html @@ -0,0 +1,102 @@ + + + + + + +jumpscale.packages.polls.bottle.polls_bottle API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.polls.bottle.polls_bottle

    +
    +
    +
    + +Expand source code + +
    from bottle import Bottle
    +
    +from jumpscale.loader import j
    +from jumpscale.packages.auth.bottle.auth import admin_only, login_required
    +from jumpscale.packages.polls.chats.threefold import VOTES
    +from jumpscale.sals.chatflows.polls import all_users
    +
    +app = Bottle()
    +
    +
    +def _map_vote_results(user_votes, votes_questions):
    +    votes_data = {}
    +    for question_name, answers in user_votes.items():
    +        for question in votes_questions.values():
    +            if question["title"] == question_name:
    +                answer = answers.index(1)
    +                votes_data[question_name] = question["options"][answer]
    +    return votes_data
    +
    +
    +@app.route("/api/results")
    +@login_required
    +@admin_only
    +def results():
    +    votes_data = {}
    +    for voter_name in all_users.list_all():
    +        voter = all_users.get(voter_name)
    +        votes_data[j.data.text.removeprefix(voter_name, "threefold_")] = _map_vote_results(voter.vote_data, VOTES)
    +
    +    votes_data["-Number of voters"] = all_users.count
    +    return votes_data
    +
    +
    +@app.route("/api/names")
    +@login_required
    +@admin_only
    +def names():
    +    data = {"names": []}
    +    for voter_name in all_users.list_all():
    +        voter = all_users.get(voter_name)
    +        tname = j.data.text.removeprefix(voter_name, "threefold_")
    +        data["names"].append(voter.extra_data.get("full_name", tname))
    +    data["-Number of voters"] = all_users.count
    +    return data
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/chats/example2.html b/docs/api/jumpscale/packages/polls/chats/example2.html new file mode 100644 index 000000000..a935dfcb6 --- /dev/null +++ b/docs/api/jumpscale/packages/polls/chats/example2.html @@ -0,0 +1,1024 @@ + + + + + + +jumpscale.packages.polls.chats.example2 API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.polls.chats.example2

    +
    +
    +
    + +Expand source code + +
    from jumpscale.sals.chatflows.polls import Poll
    +
    +
    +VOTES = {
    +    1: {
    +        "title": "Reading June 2020 update document",
    +        "content": "### It's very important that you as a ThreeFold token holder (TFTA) have read our latest update document on https://wiki.threefold.io/#/threefold_update_june2020.md",
    +        "options": [
    +            "I have read the June 2020 update document",
    +            "I have not read the June 2020 update document"
    +        ]
    +    },
    +    2: {
    +        "title": "Reading the manifesto",
    +        "content": "### It's very important that you as a ThreeFold token holder (TFTA) have read and agree with the decentralization manifesto of our TFGrid. This manifesto is the basis of our further evolution and needs to be accepted by all of us.",
    +        "options": [
    +            " I have read the manifesto and I do agree.",
    +            "I have not read the manifesto or I do not agree."
    +        ]
    +    },
    +    3: {
    +        "title": "TFTA on Stellar rights",
    +        "content": """
    +        TFTA on Stellar has all the same rights and more compared to the TFT on Rivine.<br>
    +
    +        I can
    +
    +        - Buy any capacity on the TF Grid, which represents the main use-case of this token. <br>
    +        - SellTFTA to anyone (directly or using Stellar Exchange or using atomic swaps). <br>
    +        - Transfer TFTA to anyone. <br>
    +        - Enjoy any other feature you would expect from a digital currency. <br>
    +
    +        No rights have been taken away from me by switching blockchains.
    +        """,
    +        "options": [
    +            "I do agree",
    +            "I do not agree"
    +        ]
    +    },
    +    4: {
    +        "title": "test",
    +        "content": "### The following vote is incredibly important, do realize that if we bring the TFTA on the public exchanges without price protection that there is a big probability that the price will drop way below USD 0.15. The TF Foundation believes that by growing our demand organically and executing the steps as outlined in our update document the token will get liquidity in a stable and organic way. Please keep in mind that the token is only 2 years and 2 months old. If there is no price protection we will have no choice than to stop with the TDE which means the TF Foundation will have no funding to continue and the planned promotion activities will stop. This will also mean that we will not go for the option of using onboarding tokens & partnerships like Dash & DigiByte which would allow us to go 100% decentralized for exchanging TFT to any of these onboarding tokens.",
    +        "options": [
    +            "I am fine with the option to sell my TFTA (TFTv1) on the Stellar exchange or any other decentralized market mechanism and get automatic conversion to TFTv2 end of the year.",
    +            "I want my TFTA to be available on supported exchanges as TFT and agree with minimal price protection (0.15 USD, +2% increase per month starting with May 1), sales will happen through a sales bot.",
    +            "I want my TFTA to be available on the 3 supported exchanges as TFT and there should be no price protection. I do realize this choice has the potential to damage the ThreeFold movement."
    +        ]
    +    }
    +}
    +
    +
    +
    +
    +class Example2(Poll):
    +    poll_name = "example"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.QUESTIONS = {}
    +        self.extra_data = {}
    +        self.custom_answers = {}
    +
    +    def welcome(self):
    +        
    +        statement_1 = """
    +        Dear ThreeFold Token Holder, 
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +        This poll is only for TFT v1 holders (TFTA).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +        To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +        Only the end results will be visible by the general community which is
    +
    +        - The voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +        INSERT: GDPR disclaimer (see Pierre)
    +        """
    +        
    +        self.md_show(statement_1, md=True)
    +
    +        full_name = self.string_ask("What is your full name ?", required=True)
    +        self.extra_data.update({"full_name": full_name})
    +
    +        statement_2 = """
    +        Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +        Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +        """
    +        self.md_show(statement_2, md=True)
    +
    +        question_1 = """
    +        Mark all which is relevant how you got your tokens
    +
    +        This is confidential information and is only visible to the TFGrid Council.
    +        """
    +
    +        question_1_choices = [
    +            "I bought TFT from the market, which means through atomic swap, a public exchange or from any other TFT holder",
    +            "I bought my TFT from Mazraa (ThreeFold FZC) = part of TF Foundation",
    +            "I bought my TFT from BetterToken = part of TF Foundation",
    +            "Gift from TF Foundation",
    +        ]
    +
    +        question_1_answer = self.multi_choice(question_1, options=question_1_choices, md=True, required=True)
    +        self.extra_data.update({"question_1": question_1_answer})
    +
    +        question_2 = "For every selected option above let us please now the percentage of your total amount of  TFT (if more than 1 option)"
    +        question_2_answer = self.int_ask(question_2, md=True, required=True)
    +        self.extra_data.update({"question_2": question_2_answer})
    +
    +
    +    def custom_votes(self):
    +        super().custom_votes()
    +
    +        vote_1_answer = self.single_choice(VOTES[1]["content"], VOTES[1]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[1]["title"]: VOTES[1]["options"]})
    +        self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +        vote_2_answer = self.single_choice(VOTES[2]["content"], VOTES[2]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[2]["title"]: VOTES[2]["options"]})
    +        self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +        if vote_1_answer == VOTES[1]["options"][1] or vote_2_answer == VOTES[2]["options"][1]:
    +            self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +        else: 
    +            self.md_show("Thank you for confirming our \"decentralization manifesto\", you have now digitally signed this document.")
    +
    +        vote_3_answer = self.single_choice(VOTES[3]["content"], VOTES[3]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[3]["title"]: VOTES[3]["options"]})
    +        self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +        vote_4_answer = self.single_choice(VOTES[4]["content"], VOTES[4]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[4]["title"]: VOTES[4]["options"]})
    +        self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +        self.vote()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +        # vote_1_name = "Reading June 2020 update document"
    +        # vote_1_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read our latest update document on https://wiki.threefold.io/#/threefold_update_june2020.md"
    +        # vote_1_options = [
    +        #     "I have read the June 2020 update document",
    +        #     "I have not read the June 2020 update document"
    +        # ]
    +        # self.QUESTIONS.update({vote_1_name: vote_1_options})
    +        # vote_1_answer = self.single_choice(vote_1_text, vote_1_options, md=True, required=True)
    +        # self.custom_answers.update({vote_1_name: vote_1_answer})
    +
    +
    +        # vote_2_name = "Reading the manifesto"
    +        # vote_2_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read and agree with the decentralization [manifesto](http://decentralization2.threefold.io) of our TFGrid. This manifesto is the basis of our further evolution and needs to be accepted by all of us."
    +        # vote_2_options = [
    +        #     " I have read the manifesto and I do agree.",
    +        #     "I have not read the manifesto or I do not agree."
    +        # ]
    +        
    +        # self.QUESTIONS.update({vote_2_name: vote_2_options})
    +        # vote_2_answer = self.single_choice(vote_2_text, vote_2_options, md=True, required=True)
    +        # self.custom_answers.update({vote_2_name: vote_2_answer})
    +
    +        # if vote_1_answer == vote_1_options[1] or vote_2_answer == vote_2_options[1]:
    +        #     self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +
    +
    +        # vote_3_name = "TFTA on Stellar rights"
    +        # vote_3_text = """TFTA on Stellar has all the same rights and more compared to the TFT on Rivine. <br></br>I can<br></br>
    +        # - Buy any capacity on the TF Grid, which represents the main use-case of this token.<br></br>
    +        # - SellTFTA to anyone (directly or using Stellar Exchange or using atomic swaps).<br></br>
    +        # - Transfer TFTA to anyone.<br></br>
    +        # - Enjoy any other feature you would expect from a digital currency.<br></br>
    +        # No rights have been taken away from me by switching blockchains.
    +        # """
    +        # vote_3_options = ["I do agree", "I do not agree"]
    +        # self.QUESTIONS.update({vote_3_name: vote_3_options})
    +        # vote_3_answer = self.single_choice(vote_3_text, vote_3_options, md=True, required=True)
    +        # self.custom_answers.update({vote_3_name: vote_3_answer})
    +
    +
    +        # vote_4_name = "test"
    +        # vote_4_text = """
    +        # The following vote is incredibly important, do realize that if we bring the TFTA on the public exchanges without price protection that there is a big probability that the price will drop way below USD 0.15. The TF Foundation believes that by growing our demand organically and executing the steps as outlined in our update document the token will get liquidity in a stable and organic way. Please keep in mind that the token is only 2 years and 2 months old. If there is no price protection we will have no choice than to stop with the TDE which means the TF Foundation will have no funding to continue and the planned promotion activities will stop. This will also mean that we will not go for the option of using onboarding tokens & partnerships like Dash & DigiByte which would allow us to go 100% decentralized for exchanging TFT to any of these onboarding tokens.
    +        # """
    +        # vote_4_options = [
    +        #     "I am fine with the option to sell my TFTA (TFTv1) on the Stellar exchange or any other decentralized market mechanism and get automatic conversion to TFTv2 end of the year.",
    +        #     "I want my TFTA to be available on supported exchanges as TFT and agree with minimal price protection (0.15 USD, +2% increase per month starting with May 1), sales will happen through a sales bot.",
    +        #     "I want my TFTA to be available on the 3 supported exchanges as TFT and there should be no price protection. I do realize this choice has the potential to damage the ThreeFold movement."
    +        # ]
    +        # self.QUESTIONS.update({vote_4_name: vote_4_options})
    +        # vote_4_answer = self.single_choice(vote_4_text, vote_4_options, required=True)
    +        # self.custom_answers.update({vote_4_name: vote_4_answer})
    +
    +        
    +
    +
    +chat = Example2
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Example2 +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class Example2(Poll):
    +    poll_name = "example"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.QUESTIONS = {}
    +        self.extra_data = {}
    +        self.custom_answers = {}
    +
    +    def welcome(self):
    +        
    +        statement_1 = """
    +        Dear ThreeFold Token Holder, 
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +        This poll is only for TFT v1 holders (TFTA).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +        To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +        Only the end results will be visible by the general community which is
    +
    +        - The voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +        INSERT: GDPR disclaimer (see Pierre)
    +        """
    +        
    +        self.md_show(statement_1, md=True)
    +
    +        full_name = self.string_ask("What is your full name ?", required=True)
    +        self.extra_data.update({"full_name": full_name})
    +
    +        statement_2 = """
    +        Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +        Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +        """
    +        self.md_show(statement_2, md=True)
    +
    +        question_1 = """
    +        Mark all which is relevant how you got your tokens
    +
    +        This is confidential information and is only visible to the TFGrid Council.
    +        """
    +
    +        question_1_choices = [
    +            "I bought TFT from the market, which means through atomic swap, a public exchange or from any other TFT holder",
    +            "I bought my TFT from Mazraa (ThreeFold FZC) = part of TF Foundation",
    +            "I bought my TFT from BetterToken = part of TF Foundation",
    +            "Gift from TF Foundation",
    +        ]
    +
    +        question_1_answer = self.multi_choice(question_1, options=question_1_choices, md=True, required=True)
    +        self.extra_data.update({"question_1": question_1_answer})
    +
    +        question_2 = "For every selected option above let us please now the percentage of your total amount of  TFT (if more than 1 option)"
    +        question_2_answer = self.int_ask(question_2, md=True, required=True)
    +        self.extra_data.update({"question_2": question_2_answer})
    +
    +
    +    def custom_votes(self):
    +        super().custom_votes()
    +
    +        vote_1_answer = self.single_choice(VOTES[1]["content"], VOTES[1]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[1]["title"]: VOTES[1]["options"]})
    +        self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +        vote_2_answer = self.single_choice(VOTES[2]["content"], VOTES[2]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[2]["title"]: VOTES[2]["options"]})
    +        self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +        if vote_1_answer == VOTES[1]["options"][1] or vote_2_answer == VOTES[2]["options"][1]:
    +            self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +        else: 
    +            self.md_show("Thank you for confirming our \"decentralization manifesto\", you have now digitally signed this document.")
    +
    +        vote_3_answer = self.single_choice(VOTES[3]["content"], VOTES[3]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[3]["title"]: VOTES[3]["options"]})
    +        self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +        vote_4_answer = self.single_choice(VOTES[4]["content"], VOTES[4]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[4]["title"]: VOTES[4]["options"]})
    +        self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +        self.vote()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +        # vote_1_name = "Reading June 2020 update document"
    +        # vote_1_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read our latest update document on https://wiki.threefold.io/#/threefold_update_june2020.md"
    +        # vote_1_options = [
    +        #     "I have read the June 2020 update document",
    +        #     "I have not read the June 2020 update document"
    +        # ]
    +        # self.QUESTIONS.update({vote_1_name: vote_1_options})
    +        # vote_1_answer = self.single_choice(vote_1_text, vote_1_options, md=True, required=True)
    +        # self.custom_answers.update({vote_1_name: vote_1_answer})
    +
    +
    +        # vote_2_name = "Reading the manifesto"
    +        # vote_2_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read and agree with the decentralization [manifesto](http://decentralization2.threefold.io) of our TFGrid. This manifesto is the basis of our further evolution and needs to be accepted by all of us."
    +        # vote_2_options = [
    +        #     " I have read the manifesto and I do agree.",
    +        #     "I have not read the manifesto or I do not agree."
    +        # ]
    +        
    +        # self.QUESTIONS.update({vote_2_name: vote_2_options})
    +        # vote_2_answer = self.single_choice(vote_2_text, vote_2_options, md=True, required=True)
    +        # self.custom_answers.update({vote_2_name: vote_2_answer})
    +
    +        # if vote_1_answer == vote_1_options[1] or vote_2_answer == vote_2_options[1]:
    +        #     self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +
    +
    +        # vote_3_name = "TFTA on Stellar rights"
    +        # vote_3_text = """TFTA on Stellar has all the same rights and more compared to the TFT on Rivine. <br></br>I can<br></br>
    +        # - Buy any capacity on the TF Grid, which represents the main use-case of this token.<br></br>
    +        # - SellTFTA to anyone (directly or using Stellar Exchange or using atomic swaps).<br></br>
    +        # - Transfer TFTA to anyone.<br></br>
    +        # - Enjoy any other feature you would expect from a digital currency.<br></br>
    +        # No rights have been taken away from me by switching blockchains.
    +        # """
    +        # vote_3_options = ["I do agree", "I do not agree"]
    +        # self.QUESTIONS.update({vote_3_name: vote_3_options})
    +        # vote_3_answer = self.single_choice(vote_3_text, vote_3_options, md=True, required=True)
    +        # self.custom_answers.update({vote_3_name: vote_3_answer})
    +
    +
    +        # vote_4_name = "test"
    +        # vote_4_text = """
    +        # The following vote is incredibly important, do realize that if we bring the TFTA on the public exchanges without price protection that there is a big probability that the price will drop way below USD 0.15. The TF Foundation believes that by growing our demand organically and executing the steps as outlined in our update document the token will get liquidity in a stable and organic way. Please keep in mind that the token is only 2 years and 2 months old. If there is no price protection we will have no choice than to stop with the TDE which means the TF Foundation will have no funding to continue and the planned promotion activities will stop. This will also mean that we will not go for the option of using onboarding tokens & partnerships like Dash & DigiByte which would allow us to go 100% decentralized for exchanging TFT to any of these onboarding tokens.
    +        # """
    +        # vote_4_options = [
    +        #     "I am fine with the option to sell my TFTA (TFTv1) on the Stellar exchange or any other decentralized market mechanism and get automatic conversion to TFTv2 end of the year.",
    +        #     "I want my TFTA to be available on supported exchanges as TFT and agree with minimal price protection (0.15 USD, +2% increase per month starting with May 1), sales will happen through a sales bot.",
    +        #     "I want my TFTA to be available on the 3 supported exchanges as TFT and there should be no price protection. I do realize this choice has the potential to damage the ThreeFold movement."
    +        # ]
    +        # self.QUESTIONS.update({vote_4_name: vote_4_options})
    +        # vote_4_answer = self.single_choice(vote_4_text, vote_4_options, required=True)
    +        # self.custom_answers.update({vote_4_name: vote_4_answer})
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var poll_name
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def custom_votes(self) +
    +
    +
    +
    + +Expand source code + +
    def custom_votes(self):
    +    super().custom_votes()
    +
    +    vote_1_answer = self.single_choice(VOTES[1]["content"], VOTES[1]["options"], md=True, required=True)
    +    self.QUESTIONS.update({VOTES[1]["title"]: VOTES[1]["options"]})
    +    self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +    vote_2_answer = self.single_choice(VOTES[2]["content"], VOTES[2]["options"], md=True, required=True)
    +    self.QUESTIONS.update({VOTES[2]["title"]: VOTES[2]["options"]})
    +    self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +    if vote_1_answer == VOTES[1]["options"][1] or vote_2_answer == VOTES[2]["options"][1]:
    +        self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +    else: 
    +        self.md_show("Thank you for confirming our \"decentralization manifesto\", you have now digitally signed this document.")
    +
    +    vote_3_answer = self.single_choice(VOTES[3]["content"], VOTES[3]["options"], md=True, required=True)
    +    self.QUESTIONS.update({VOTES[3]["title"]: VOTES[3]["options"]})
    +    self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +    vote_4_answer = self.single_choice(VOTES[4]["content"], VOTES[4]["options"], md=True, required=True)
    +    self.QUESTIONS.update({VOTES[4]["title"]: VOTES[4]["options"]})
    +    self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +    self.vote()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +    # vote_1_name = "Reading June 2020 update document"
    +    # vote_1_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read our latest update document on https://wiki.threefold.io/#/threefold_update_june2020.md"
    +    # vote_1_options = [
    +    #     "I have read the June 2020 update document",
    +    #     "I have not read the June 2020 update document"
    +    # ]
    +    # self.QUESTIONS.update({vote_1_name: vote_1_options})
    +    # vote_1_answer = self.single_choice(vote_1_text, vote_1_options, md=True, required=True)
    +    # self.custom_answers.update({vote_1_name: vote_1_answer})
    +
    +
    +    # vote_2_name = "Reading the manifesto"
    +    # vote_2_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read and agree with the decentralization [manifesto](http://decentralization2.threefold.io) of our TFGrid. This manifesto is the basis of our further evolution and needs to be accepted by all of us."
    +    # vote_2_options = [
    +    #     " I have read the manifesto and I do agree.",
    +    #     "I have not read the manifesto or I do not agree."
    +    # ]
    +    
    +    # self.QUESTIONS.update({vote_2_name: vote_2_options})
    +    # vote_2_answer = self.single_choice(vote_2_text, vote_2_options, md=True, required=True)
    +    # self.custom_answers.update({vote_2_name: vote_2_answer})
    +
    +    # if vote_1_answer == vote_1_options[1] or vote_2_answer == vote_2_options[1]:
    +    #     self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +
    +
    +    # vote_3_name = "TFTA on Stellar rights"
    +    # vote_3_text = """TFTA on Stellar has all the same rights and more compared to the TFT on Rivine. <br></br>I can<br></br>
    +    # - Buy any capacity on the TF Grid, which represents the main use-case of this token.<br></br>
    +    # - SellTFTA to anyone (directly or using Stellar Exchange or using atomic swaps).<br></br>
    +    # - Transfer TFTA to anyone.<br></br>
    +    # - Enjoy any other feature you would expect from a digital currency.<br></br>
    +    # No rights have been taken away from me by switching blockchains.
    +    # """
    +    # vote_3_options = ["I do agree", "I do not agree"]
    +    # self.QUESTIONS.update({vote_3_name: vote_3_options})
    +    # vote_3_answer = self.single_choice(vote_3_text, vote_3_options, md=True, required=True)
    +    # self.custom_answers.update({vote_3_name: vote_3_answer})
    +
    +
    +    # vote_4_name = "test"
    +    # vote_4_text = """
    +    # The following vote is incredibly important, do realize that if we bring the TFTA on the public exchanges without price protection that there is a big probability that the price will drop way below USD 0.15. The TF Foundation believes that by growing our demand organically and executing the steps as outlined in our update document the token will get liquidity in a stable and organic way. Please keep in mind that the token is only 2 years and 2 months old. If there is no price protection we will have no choice than to stop with the TDE which means the TF Foundation will have no funding to continue and the planned promotion activities will stop. This will also mean that we will not go for the option of using onboarding tokens & partnerships like Dash & DigiByte which would allow us to go 100% decentralized for exchanging TFT to any of these onboarding tokens.
    +    # """
    +    # vote_4_options = [
    +    #     "I am fine with the option to sell my TFTA (TFTv1) on the Stellar exchange or any other decentralized market mechanism and get automatic conversion to TFTv2 end of the year.",
    +    #     "I want my TFTA to be available on supported exchanges as TFT and agree with minimal price protection (0.15 USD, +2% increase per month starting with May 1), sales will happen through a sales bot.",
    +    #     "I want my TFTA to be available on the 3 supported exchanges as TFT and there should be no price protection. I do realize this choice has the potential to damage the ThreeFold movement."
    +    # ]
    +    # self.QUESTIONS.update({vote_4_name: vote_4_options})
    +    # vote_4_answer = self.single_choice(vote_4_text, vote_4_options, required=True)
    +    # self.custom_answers.update({vote_4_name: vote_4_answer})
    +
    +
    +
    +def welcome(self) +
    +
    +
    +
    + +Expand source code + +
    def welcome(self):
    +    
    +    statement_1 = """
    +    Dear ThreeFold Token Holder, 
    +
    +    This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +    This poll is only for TFT v1 holders (TFTA).
    +
    +    This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +    To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +    The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +    Only the end results will be visible by the general community which is
    +
    +    - The voting questions (comes at end of this poll)
    +    - % of votes as results per question, weighted and unweighted
    +    - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +    INSERT: GDPR disclaimer (see Pierre)
    +    """
    +    
    +    self.md_show(statement_1, md=True)
    +
    +    full_name = self.string_ask("What is your full name ?", required=True)
    +    self.extra_data.update({"full_name": full_name})
    +
    +    statement_2 = """
    +    Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +    Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +    """
    +    self.md_show(statement_2, md=True)
    +
    +    question_1 = """
    +    Mark all which is relevant how you got your tokens
    +
    +    This is confidential information and is only visible to the TFGrid Council.
    +    """
    +
    +    question_1_choices = [
    +        "I bought TFT from the market, which means through atomic swap, a public exchange or from any other TFT holder",
    +        "I bought my TFT from Mazraa (ThreeFold FZC) = part of TF Foundation",
    +        "I bought my TFT from BetterToken = part of TF Foundation",
    +        "Gift from TF Foundation",
    +    ]
    +
    +    question_1_answer = self.multi_choice(question_1, options=question_1_choices, md=True, required=True)
    +    self.extra_data.update({"question_1": question_1_answer})
    +
    +    question_2 = "For every selected option above let us please now the percentage of your total amount of  TFT (if more than 1 option)"
    +    question_2_answer = self.int_ask(question_2, md=True, required=True)
    +    self.extra_data.update({"question_2": question_2_answer})
    +
    +
    +
    +
    +
    +class chat +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class Example2(Poll):
    +    poll_name = "example"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.QUESTIONS = {}
    +        self.extra_data = {}
    +        self.custom_answers = {}
    +
    +    def welcome(self):
    +        
    +        statement_1 = """
    +        Dear ThreeFold Token Holder, 
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +        This poll is only for TFT v1 holders (TFTA).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +        To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +        Only the end results will be visible by the general community which is
    +
    +        - The voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +        INSERT: GDPR disclaimer (see Pierre)
    +        """
    +        
    +        self.md_show(statement_1, md=True)
    +
    +        full_name = self.string_ask("What is your full name ?", required=True)
    +        self.extra_data.update({"full_name": full_name})
    +
    +        statement_2 = """
    +        Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +        Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +        """
    +        self.md_show(statement_2, md=True)
    +
    +        question_1 = """
    +        Mark all which is relevant how you got your tokens
    +
    +        This is confidential information and is only visible to the TFGrid Council.
    +        """
    +
    +        question_1_choices = [
    +            "I bought TFT from the market, which means through atomic swap, a public exchange or from any other TFT holder",
    +            "I bought my TFT from Mazraa (ThreeFold FZC) = part of TF Foundation",
    +            "I bought my TFT from BetterToken = part of TF Foundation",
    +            "Gift from TF Foundation",
    +        ]
    +
    +        question_1_answer = self.multi_choice(question_1, options=question_1_choices, md=True, required=True)
    +        self.extra_data.update({"question_1": question_1_answer})
    +
    +        question_2 = "For every selected option above let us please now the percentage of your total amount of  TFT (if more than 1 option)"
    +        question_2_answer = self.int_ask(question_2, md=True, required=True)
    +        self.extra_data.update({"question_2": question_2_answer})
    +
    +
    +    def custom_votes(self):
    +        super().custom_votes()
    +
    +        vote_1_answer = self.single_choice(VOTES[1]["content"], VOTES[1]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[1]["title"]: VOTES[1]["options"]})
    +        self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +        vote_2_answer = self.single_choice(VOTES[2]["content"], VOTES[2]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[2]["title"]: VOTES[2]["options"]})
    +        self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +        if vote_1_answer == VOTES[1]["options"][1] or vote_2_answer == VOTES[2]["options"][1]:
    +            self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +        else: 
    +            self.md_show("Thank you for confirming our \"decentralization manifesto\", you have now digitally signed this document.")
    +
    +        vote_3_answer = self.single_choice(VOTES[3]["content"], VOTES[3]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[3]["title"]: VOTES[3]["options"]})
    +        self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +        vote_4_answer = self.single_choice(VOTES[4]["content"], VOTES[4]["options"], md=True, required=True)
    +        self.QUESTIONS.update({VOTES[4]["title"]: VOTES[4]["options"]})
    +        self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +        self.vote()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +        # vote_1_name = "Reading June 2020 update document"
    +        # vote_1_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read our latest update document on https://wiki.threefold.io/#/threefold_update_june2020.md"
    +        # vote_1_options = [
    +        #     "I have read the June 2020 update document",
    +        #     "I have not read the June 2020 update document"
    +        # ]
    +        # self.QUESTIONS.update({vote_1_name: vote_1_options})
    +        # vote_1_answer = self.single_choice(vote_1_text, vote_1_options, md=True, required=True)
    +        # self.custom_answers.update({vote_1_name: vote_1_answer})
    +
    +
    +        # vote_2_name = "Reading the manifesto"
    +        # vote_2_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read and agree with the decentralization [manifesto](http://decentralization2.threefold.io) of our TFGrid. This manifesto is the basis of our further evolution and needs to be accepted by all of us."
    +        # vote_2_options = [
    +        #     " I have read the manifesto and I do agree.",
    +        #     "I have not read the manifesto or I do not agree."
    +        # ]
    +        
    +        # self.QUESTIONS.update({vote_2_name: vote_2_options})
    +        # vote_2_answer = self.single_choice(vote_2_text, vote_2_options, md=True, required=True)
    +        # self.custom_answers.update({vote_2_name: vote_2_answer})
    +
    +        # if vote_1_answer == vote_1_options[1] or vote_2_answer == vote_2_options[1]:
    +        #     self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +
    +
    +        # vote_3_name = "TFTA on Stellar rights"
    +        # vote_3_text = """TFTA on Stellar has all the same rights and more compared to the TFT on Rivine. <br></br>I can<br></br>
    +        # - Buy any capacity on the TF Grid, which represents the main use-case of this token.<br></br>
    +        # - SellTFTA to anyone (directly or using Stellar Exchange or using atomic swaps).<br></br>
    +        # - Transfer TFTA to anyone.<br></br>
    +        # - Enjoy any other feature you would expect from a digital currency.<br></br>
    +        # No rights have been taken away from me by switching blockchains.
    +        # """
    +        # vote_3_options = ["I do agree", "I do not agree"]
    +        # self.QUESTIONS.update({vote_3_name: vote_3_options})
    +        # vote_3_answer = self.single_choice(vote_3_text, vote_3_options, md=True, required=True)
    +        # self.custom_answers.update({vote_3_name: vote_3_answer})
    +
    +
    +        # vote_4_name = "test"
    +        # vote_4_text = """
    +        # The following vote is incredibly important, do realize that if we bring the TFTA on the public exchanges without price protection that there is a big probability that the price will drop way below USD 0.15. The TF Foundation believes that by growing our demand organically and executing the steps as outlined in our update document the token will get liquidity in a stable and organic way. Please keep in mind that the token is only 2 years and 2 months old. If there is no price protection we will have no choice than to stop with the TDE which means the TF Foundation will have no funding to continue and the planned promotion activities will stop. This will also mean that we will not go for the option of using onboarding tokens & partnerships like Dash & DigiByte which would allow us to go 100% decentralized for exchanging TFT to any of these onboarding tokens.
    +        # """
    +        # vote_4_options = [
    +        #     "I am fine with the option to sell my TFTA (TFTv1) on the Stellar exchange or any other decentralized market mechanism and get automatic conversion to TFTv2 end of the year.",
    +        #     "I want my TFTA to be available on supported exchanges as TFT and agree with minimal price protection (0.15 USD, +2% increase per month starting with May 1), sales will happen through a sales bot.",
    +        #     "I want my TFTA to be available on the 3 supported exchanges as TFT and there should be no price protection. I do realize this choice has the potential to damage the ThreeFold movement."
    +        # ]
    +        # self.QUESTIONS.update({vote_4_name: vote_4_options})
    +        # vote_4_answer = self.single_choice(vote_4_text, vote_4_options, required=True)
    +        # self.custom_answers.update({vote_4_name: vote_4_answer})
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var poll_name
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def custom_votes(self) +
    +
    +
    +
    + +Expand source code + +
    def custom_votes(self):
    +    super().custom_votes()
    +
    +    vote_1_answer = self.single_choice(VOTES[1]["content"], VOTES[1]["options"], md=True, required=True)
    +    self.QUESTIONS.update({VOTES[1]["title"]: VOTES[1]["options"]})
    +    self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +    vote_2_answer = self.single_choice(VOTES[2]["content"], VOTES[2]["options"], md=True, required=True)
    +    self.QUESTIONS.update({VOTES[2]["title"]: VOTES[2]["options"]})
    +    self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +    if vote_1_answer == VOTES[1]["options"][1] or vote_2_answer == VOTES[2]["options"][1]:
    +        self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +    else: 
    +        self.md_show("Thank you for confirming our \"decentralization manifesto\", you have now digitally signed this document.")
    +
    +    vote_3_answer = self.single_choice(VOTES[3]["content"], VOTES[3]["options"], md=True, required=True)
    +    self.QUESTIONS.update({VOTES[3]["title"]: VOTES[3]["options"]})
    +    self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +    vote_4_answer = self.single_choice(VOTES[4]["content"], VOTES[4]["options"], md=True, required=True)
    +    self.QUESTIONS.update({VOTES[4]["title"]: VOTES[4]["options"]})
    +    self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +    self.vote()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +    # vote_1_name = "Reading June 2020 update document"
    +    # vote_1_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read our latest update document on https://wiki.threefold.io/#/threefold_update_june2020.md"
    +    # vote_1_options = [
    +    #     "I have read the June 2020 update document",
    +    #     "I have not read the June 2020 update document"
    +    # ]
    +    # self.QUESTIONS.update({vote_1_name: vote_1_options})
    +    # vote_1_answer = self.single_choice(vote_1_text, vote_1_options, md=True, required=True)
    +    # self.custom_answers.update({vote_1_name: vote_1_answer})
    +
    +
    +    # vote_2_name = "Reading the manifesto"
    +    # vote_2_text = "### It's very important that you as a ThreeFold token holder (TFTA) have read and agree with the decentralization [manifesto](http://decentralization2.threefold.io) of our TFGrid. This manifesto is the basis of our further evolution and needs to be accepted by all of us."
    +    # vote_2_options = [
    +    #     " I have read the manifesto and I do agree.",
    +    #     "I have not read the manifesto or I do not agree."
    +    # ]
    +    
    +    # self.QUESTIONS.update({vote_2_name: vote_2_options})
    +    # vote_2_answer = self.single_choice(vote_2_text, vote_2_options, md=True, required=True)
    +    # self.custom_answers.update({vote_2_name: vote_2_answer})
    +
    +    # if vote_1_answer == vote_1_options[1] or vote_2_answer == vote_2_options[1]:
    +    #     self.stop("Thank you for your participation, The poll ends now because you did read the June 2020 update document and/or did not read the manifesto.")
    +
    +
    +    # vote_3_name = "TFTA on Stellar rights"
    +    # vote_3_text = """TFTA on Stellar has all the same rights and more compared to the TFT on Rivine. <br></br>I can<br></br>
    +    # - Buy any capacity on the TF Grid, which represents the main use-case of this token.<br></br>
    +    # - SellTFTA to anyone (directly or using Stellar Exchange or using atomic swaps).<br></br>
    +    # - Transfer TFTA to anyone.<br></br>
    +    # - Enjoy any other feature you would expect from a digital currency.<br></br>
    +    # No rights have been taken away from me by switching blockchains.
    +    # """
    +    # vote_3_options = ["I do agree", "I do not agree"]
    +    # self.QUESTIONS.update({vote_3_name: vote_3_options})
    +    # vote_3_answer = self.single_choice(vote_3_text, vote_3_options, md=True, required=True)
    +    # self.custom_answers.update({vote_3_name: vote_3_answer})
    +
    +
    +    # vote_4_name = "test"
    +    # vote_4_text = """
    +    # The following vote is incredibly important, do realize that if we bring the TFTA on the public exchanges without price protection that there is a big probability that the price will drop way below USD 0.15. The TF Foundation believes that by growing our demand organically and executing the steps as outlined in our update document the token will get liquidity in a stable and organic way. Please keep in mind that the token is only 2 years and 2 months old. If there is no price protection we will have no choice than to stop with the TDE which means the TF Foundation will have no funding to continue and the planned promotion activities will stop. This will also mean that we will not go for the option of using onboarding tokens & partnerships like Dash & DigiByte which would allow us to go 100% decentralized for exchanging TFT to any of these onboarding tokens.
    +    # """
    +    # vote_4_options = [
    +    #     "I am fine with the option to sell my TFTA (TFTv1) on the Stellar exchange or any other decentralized market mechanism and get automatic conversion to TFTv2 end of the year.",
    +    #     "I want my TFTA to be available on supported exchanges as TFT and agree with minimal price protection (0.15 USD, +2% increase per month starting with May 1), sales will happen through a sales bot.",
    +    #     "I want my TFTA to be available on the 3 supported exchanges as TFT and there should be no price protection. I do realize this choice has the potential to damage the ThreeFold movement."
    +    # ]
    +    # self.QUESTIONS.update({vote_4_name: vote_4_options})
    +    # vote_4_answer = self.single_choice(vote_4_text, vote_4_options, required=True)
    +    # self.custom_answers.update({vote_4_name: vote_4_answer})
    +
    +
    +
    +def welcome(self) +
    +
    +
    +
    + +Expand source code + +
    def welcome(self):
    +    
    +    statement_1 = """
    +    Dear ThreeFold Token Holder, 
    +
    +    This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +    This poll is only for TFT v1 holders (TFTA).
    +
    +    This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +    To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +    The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +    Only the end results will be visible by the general community which is
    +
    +    - The voting questions (comes at end of this poll)
    +    - % of votes as results per question, weighted and unweighted
    +    - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +    INSERT: GDPR disclaimer (see Pierre)
    +    """
    +    
    +    self.md_show(statement_1, md=True)
    +
    +    full_name = self.string_ask("What is your full name ?", required=True)
    +    self.extra_data.update({"full_name": full_name})
    +
    +    statement_2 = """
    +    Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +    Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +    """
    +    self.md_show(statement_2, md=True)
    +
    +    question_1 = """
    +    Mark all which is relevant how you got your tokens
    +
    +    This is confidential information and is only visible to the TFGrid Council.
    +    """
    +
    +    question_1_choices = [
    +        "I bought TFT from the market, which means through atomic swap, a public exchange or from any other TFT holder",
    +        "I bought my TFT from Mazraa (ThreeFold FZC) = part of TF Foundation",
    +        "I bought my TFT from BetterToken = part of TF Foundation",
    +        "Gift from TF Foundation",
    +    ]
    +
    +    question_1_answer = self.multi_choice(question_1, options=question_1_choices, md=True, required=True)
    +    self.extra_data.update({"question_1": question_1_answer})
    +
    +    question_2 = "For every selected option above let us please now the percentage of your total amount of  TFT (if more than 1 option)"
    +    question_2_answer = self.int_ask(question_2, md=True, required=True)
    +    self.extra_data.update({"question_2": question_2_answer})
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/chats/foo.html b/docs/api/jumpscale/packages/polls/chats/foo.html new file mode 100644 index 000000000..dda07f6d1 --- /dev/null +++ b/docs/api/jumpscale/packages/polls/chats/foo.html @@ -0,0 +1,221 @@ + + + + + + +jumpscale.packages.polls.chats.foo API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.polls.chats.foo

    +
    +
    +
    + +Expand source code + +
    from jumpscale.sals.chatflows.polls import Poll
    +
    +
    +class Foo(Poll):
    +    poll_name = "foo"
    +    QUESTIONS = {
    +        "What's your favorite movie?": ["Avengers", "Lord of the rings", "Harry Potter", "something else"],
    +        "What's your favorite programing language?": ["Python", "Go", "Python bardo :P"],
    +        "Did you like the vote?": ["Yes", "No", "maybe"],
    +    }
    +
    +
    +chat = Foo
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Foo +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class Foo(Poll):
    +    poll_name = "foo"
    +    QUESTIONS = {
    +        "What's your favorite movie?": ["Avengers", "Lord of the rings", "Harry Potter", "something else"],
    +        "What's your favorite programing language?": ["Python", "Go", "Python bardo :P"],
    +        "Did you like the vote?": ["Yes", "No", "maybe"],
    +    }
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var QUESTIONS
    +
    +
    +
    +
    var poll_name
    +
    +
    +
    +
    +
    +
    +class chat +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class Foo(Poll):
    +    poll_name = "foo"
    +    QUESTIONS = {
    +        "What's your favorite movie?": ["Avengers", "Lord of the rings", "Harry Potter", "something else"],
    +        "What's your favorite programing language?": ["Python", "Go", "Python bardo :P"],
    +        "Did you like the vote?": ["Yes", "No", "maybe"],
    +    }
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var QUESTIONS
    +
    +
    +
    +
    var poll_name
    +
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/chats/index.html b/docs/api/jumpscale/packages/polls/chats/index.html new file mode 100644 index 000000000..8a630adb8 --- /dev/null +++ b/docs/api/jumpscale/packages/polls/chats/index.html @@ -0,0 +1,80 @@ + + + + + + +jumpscale.packages.polls.chats API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/chats/new.html b/docs/api/jumpscale/packages/polls/chats/new.html new file mode 100644 index 000000000..34c239e74 --- /dev/null +++ b/docs/api/jumpscale/packages/polls/chats/new.html @@ -0,0 +1,516 @@ + + + + + + +jumpscale.packages.polls.chats.new API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.polls.chats.new

    +
    +
    +
    + +Expand source code + +
    from jumpscale.sals.chatflows.polls import Poll
    +
    +
    +class New(Poll):
    +    poll_name = "new"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.QUESTIONS = {
    +            "What's your favorite color?": ["Blue", "Red", "Green", "Orange"],
    +            "What's your favorite Team?": ["Barcelona", "Manchester United", "AlAhly"],
    +            "Did you like the vote?": ["Yes", "No"],
    +        }
    +
    +    def welcome(self):
    +        statement_1 = """
    +        Dear ThreeFold Token Holder, 
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +        This poll is only for TFT v1 holders (TFTA).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +        To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +
    +        Only the end results will be visible by the general community which is
    +
    +        - the voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +        INSERT: GDPR disclaimer (see Pierre)
    +
    +        ## Get to know voting rights email addr, ...
    +
    +        - ask for more than 1 TFTA (there can be more than 1 wallet linked to the vote)
    +        - keep on asking is this your email address and the amount of TFT you want to vote with
    +        - result needs to be that user agreed that this is his email address, 3bot name and the TFT linked to the poll are ok
    +        - every time the user comes back to this poll we have to show this again and ask if correct, if not allow correction
    +        """
    +        
    +        self.md_show(statement_1, md=True)
    +
    +        statement_2 = """
    +        Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +        Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +        """
    +        x = self.multi_choice("aaaaaaaaaa", ["a", "b", "c"])
    +        self.md_show(str(x), md=True)
    +
    +
    +
    +
    +    # def custom_votes(self):
    +    #     """allow to have custom slides
    +    #     just update custom_questions, custom_answers dicts
    +    #     extra_data: save data outside the poll
    +
    +    #     Returns:
    +    #         Dict, Dict: Has all questions and answer, extra saved
    +    #     """
    +    #     custom_answers = {}
    +    #     extra_data = {}
    +
    +    #     q1 = "What's your favorite artist?"
    +    #     q1_choice = ["Amr Diab", "Adele", "Celin Dion", "Angham"]
    +    #     q1_answer = self.drop_down_choice(q1, q1_choice, required=True)
    +    #     self.QUESTIONS.update({q1: q1_choice})
    +    #     custom_answers.update({q1: q1_answer})
    +
    +    #     feedback_text = "Please provide your feedback"
    +    #     feedback = self.text_ask(feedback_text)
    +    #     extra_data.update({"feedback": feedback})
    +
    +    #     return custom_answers, extra_data
    +
    +
    +chat = New
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class New +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class New(Poll):
    +    poll_name = "new"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.QUESTIONS = {
    +            "What's your favorite color?": ["Blue", "Red", "Green", "Orange"],
    +            "What's your favorite Team?": ["Barcelona", "Manchester United", "AlAhly"],
    +            "Did you like the vote?": ["Yes", "No"],
    +        }
    +
    +    def welcome(self):
    +        statement_1 = """
    +        Dear ThreeFold Token Holder, 
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +        This poll is only for TFT v1 holders (TFTA).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +        To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +
    +        Only the end results will be visible by the general community which is
    +
    +        - the voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +        INSERT: GDPR disclaimer (see Pierre)
    +
    +        ## Get to know voting rights email addr, ...
    +
    +        - ask for more than 1 TFTA (there can be more than 1 wallet linked to the vote)
    +        - keep on asking is this your email address and the amount of TFT you want to vote with
    +        - result needs to be that user agreed that this is his email address, 3bot name and the TFT linked to the poll are ok
    +        - every time the user comes back to this poll we have to show this again and ask if correct, if not allow correction
    +        """
    +        
    +        self.md_show(statement_1, md=True)
    +
    +        statement_2 = """
    +        Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +        Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +        """
    +        x = self.multi_choice("aaaaaaaaaa", ["a", "b", "c"])
    +        self.md_show(str(x), md=True)
    +
    +
    +
    +
    +    # def custom_votes(self):
    +    #     """allow to have custom slides
    +    #     just update custom_questions, custom_answers dicts
    +    #     extra_data: save data outside the poll
    +
    +    #     Returns:
    +    #         Dict, Dict: Has all questions and answer, extra saved
    +    #     """
    +    #     custom_answers = {}
    +    #     extra_data = {}
    +
    +    #     q1 = "What's your favorite artist?"
    +    #     q1_choice = ["Amr Diab", "Adele", "Celin Dion", "Angham"]
    +    #     q1_answer = self.drop_down_choice(q1, q1_choice, required=True)
    +    #     self.QUESTIONS.update({q1: q1_choice})
    +    #     custom_answers.update({q1: q1_answer})
    +
    +    #     feedback_text = "Please provide your feedback"
    +    #     feedback = self.text_ask(feedback_text)
    +    #     extra_data.update({"feedback": feedback})
    +
    +    #     return custom_answers, extra_data
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var poll_name
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def welcome(self) +
    +
    +
    +
    + +Expand source code + +
    def welcome(self):
    +    statement_1 = """
    +    Dear ThreeFold Token Holder, 
    +
    +    This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +    This poll is only for TFT v1 holders (TFTA).
    +
    +    This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +    To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +
    +    The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +
    +    Only the end results will be visible by the general community which is
    +
    +    - the voting questions (comes at end of this poll)
    +    - % of votes as results per question, weighted and unweighted
    +    - unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +    INSERT: GDPR disclaimer (see Pierre)
    +
    +    ## Get to know voting rights email addr, ...
    +
    +    - ask for more than 1 TFTA (there can be more than 1 wallet linked to the vote)
    +    - keep on asking is this your email address and the amount of TFT you want to vote with
    +    - result needs to be that user agreed that this is his email address, 3bot name and the TFT linked to the poll are ok
    +    - every time the user comes back to this poll we have to show this again and ask if correct, if not allow correction
    +    """
    +    
    +    self.md_show(statement_1, md=True)
    +
    +    statement_2 = """
    +    Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +    Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +    """
    +    x = self.multi_choice("aaaaaaaaaa", ["a", "b", "c"])
    +    self.md_show(str(x), md=True)
    +
    +
    +
    +
    +
    +class chat +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class New(Poll):
    +    poll_name = "new"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.QUESTIONS = {
    +            "What's your favorite color?": ["Blue", "Red", "Green", "Orange"],
    +            "What's your favorite Team?": ["Barcelona", "Manchester United", "AlAhly"],
    +            "Did you like the vote?": ["Yes", "No"],
    +        }
    +
    +    def welcome(self):
    +        statement_1 = """
    +        Dear ThreeFold Token Holder, 
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +        This poll is only for TFT v1 holders (TFTA).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +        To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +
    +        Only the end results will be visible by the general community which is
    +
    +        - the voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +        INSERT: GDPR disclaimer (see Pierre)
    +
    +        ## Get to know voting rights email addr, ...
    +
    +        - ask for more than 1 TFTA (there can be more than 1 wallet linked to the vote)
    +        - keep on asking is this your email address and the amount of TFT you want to vote with
    +        - result needs to be that user agreed that this is his email address, 3bot name and the TFT linked to the poll are ok
    +        - every time the user comes back to this poll we have to show this again and ask if correct, if not allow correction
    +        """
    +        
    +        self.md_show(statement_1, md=True)
    +
    +        statement_2 = """
    +        Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +        Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +        """
    +        x = self.multi_choice("aaaaaaaaaa", ["a", "b", "c"])
    +        self.md_show(str(x), md=True)
    +
    +
    +
    +
    +    # def custom_votes(self):
    +    #     """allow to have custom slides
    +    #     just update custom_questions, custom_answers dicts
    +    #     extra_data: save data outside the poll
    +
    +    #     Returns:
    +    #         Dict, Dict: Has all questions and answer, extra saved
    +    #     """
    +    #     custom_answers = {}
    +    #     extra_data = {}
    +
    +    #     q1 = "What's your favorite artist?"
    +    #     q1_choice = ["Amr Diab", "Adele", "Celin Dion", "Angham"]
    +    #     q1_answer = self.drop_down_choice(q1, q1_choice, required=True)
    +    #     self.QUESTIONS.update({q1: q1_choice})
    +    #     custom_answers.update({q1: q1_answer})
    +
    +    #     feedback_text = "Please provide your feedback"
    +    #     feedback = self.text_ask(feedback_text)
    +    #     extra_data.update({"feedback": feedback})
    +
    +    #     return custom_answers, extra_data
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var poll_name
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def welcome(self) +
    +
    +
    +
    + +Expand source code + +
    def welcome(self):
    +    statement_1 = """
    +    Dear ThreeFold Token Holder, 
    +
    +    This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid). 
    +    This poll is only for TFT v1 holders (TFTA).
    +
    +    This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +    To understand the why and how of this poll consult: https://wiki.threefold.io/#/decentr
    +
    +    The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see https://wiki.threefold.io/#/threefold_councils.md
    +
    +    Only the end results will be visible by the general community which is
    +
    +    - the voting questions (comes at end of this poll)
    +    - % of votes as results per question, weighted and unweighted
    +    - unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.
    +
    +    INSERT: GDPR disclaimer (see Pierre)
    +
    +    ## Get to know voting rights email addr, ...
    +
    +    - ask for more than 1 TFTA (there can be more than 1 wallet linked to the vote)
    +    - keep on asking is this your email address and the amount of TFT you want to vote with
    +    - result needs to be that user agreed that this is his email address, 3bot name and the TFT linked to the poll are ok
    +    - every time the user comes back to this poll we have to show this again and ask if correct, if not allow correction
    +    """
    +    
    +    self.md_show(statement_1, md=True)
    +
    +    statement_2 = """
    +    Please read the introduction to this poll on link: https://wiki.threefold.io/#/threefold_poll_2_1.md
    +    Please read the decentralization manifesto on http://decentralization2.threefold.io  (doc not there yet now on: https://docs.google.com/document/d/1IASWZWC7N-l_JVyKpjmzrbitXjQh9wUjQxuflEzTYck/edit#)
    +    """
    +    x = self.multi_choice("aaaaaaaaaa", ["a", "b", "c"])
    +    self.md_show(str(x), md=True)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/chats/threefold.html b/docs/api/jumpscale/packages/polls/chats/threefold.html new file mode 100644 index 000000000..3af4f2c7a --- /dev/null +++ b/docs/api/jumpscale/packages/polls/chats/threefold.html @@ -0,0 +1,648 @@ + + + + + + +jumpscale.packages.polls.chats.threefold API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.polls.chats.threefold

    +
    +
    +
    + +Expand source code + +
    from jumpscale.sals.chatflows.polls import Poll, MANIFESTO_VERSION
    +from textwrap import dedent
    +
    +
    +VOTES = {
    +    1: {
    +        "title": "Reading June 2020 update document",
    +        "content": """<span>We have prepared a new update of what has been happening please go to <a href="https://wiki.threefold.io/#/threefold_update_july2020.md" target="_blank">https://wiki.threefold.io/#/threefold_update_july2020.md</a></span>""",
    +        "options": ["I have read the June 2020 update document", "I have not read the June 2020 update document"],
    +    },
    +    2: {
    +        "title": "Reading the manifesto",
    +        "content": f"""<span>It's very important that you as a ThreeFold token holder (TFTA) or TFGrid user have read and agree with the <a href="http://decentralization2.threefold.io" target="_blank">Decentralization Manifesto v{MANIFESTO_VERSION}</a> of our TFGrid. <br><br>This manifesto is the basis of our further evolution and needs to be accepted by all of us.</span>""",
    +        "options": [
    +            "I have read the manifesto and I do agree with the contents of this manifesto.",
    +            "I have not read the manifesto or I do not agree.",
    +        ],
    +    },
    +    3: {
    +        "title": "TFTA on Stellar rights",
    +        "content": dedent(
    +            """\
    +            From May 2020, TFT v1 (Rivine) users can migrate their tokens towards a new public blockchain called Stellar.
    +            Each user had to do this action him/herself.
    +            TFTv1 is called TFTA on Stellar. TFTA and TFTv1 are identical and have all the same properties.
    +            Additional benefits are: Stellar Exchange and a public blockchain with more users so more chance for liquidity.
    +
    +            I can
    +
    +            - Buy any capacity on the TF Grid, which represents the main use-case of this token.
    +            - Sell TFTA to anyone (directly or using Stellar Exchange or using atomic swaps)
    +            - Transfer TFTA to anyone
    +
    +            <br>No rights have been taken away from me by switching blockchains. Please realize this question has nothing to do in relation to Liquid and BTCAlpha public exchange integration, this is next question.
    +
    +        """
    +        ),
    +        "options": ["I do agree", "I do not agree"],
    +    },
    +    4: {
    +        "title": "TFTA Availability",
    +        "content": "The following vote is incredibly important, do realize that if we bring the TFTA on the public exchanges without price protection that there is a big probability that the price will drop way below USD 0.15.<br><br>The TF Foundation believes that by growing our demand organically and executing the steps as outlined in our update document the token will get liquidity in a stable and organic way. Please keep in mind that the token is only 2 years and 2 months old. <br><br>If there is no price protection we will have no choice than to stop with the TDE which means the TF Foundation will have no funding to continue and the planned promotion activities will stop. <br><br>This will also mean that we will not go for the option of using onboarding tokens & partnerships like Dash & DigiByte which would allow us to go 100% decentralized for exchanging TFT to any of these onboarding tokens. <br><br> The TFGrid Council and the Wisdom Council will decide how to go forward based on the public visible voting results of the question below.",
    +        "options": [
    +            "I am fine with the option to sell my TFTA (TFTv1) on the Stellar exchange or any other decentralized market mechanism and get automatic conversion to TFTv2 end of the year.",
    +            "I want my TFTA to be available on supported exchanges as TFT and agree with minimal price protection (0.15 USD, +2% increase per month starting with May 1), sales will happen through a sales bot.",
    +            "I want my TFTA to be available on supported exchanges as TFT and there should be no price protection. I do realize this choice has the potential to damage the ThreeFold movement.",
    +        ],
    +    },
    +}
    +
    +# to upgrade the titles
    +NEW_TITLE_KEYS = {
    +    "Reading June 2020 update document": "Reading June 2020 update document",
    +    "Reading the manifesto": "Reading the manifesto",
    +    "TFTA on Stellar rights": "TFTA on Stellar rights",
    +    "TFTA Availability": "TFTA trading mechanism support",
    +}
    +
    +
    +class TFPoll(Poll):
    +    poll_name = "threefold"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.extra_data = {}
    +        self.custom_answers = {}
    +        self.metadata = {"new_title_keys": NEW_TITLE_KEYS}
    +        self.QUESTIONS = {vote["title"]: vote["options"] for vote in VOTES.values()}
    +
    +    def welcome(self):
    +        stored_extra_data = self.user.extra_data
    +
    +        statement_1 = """\
    +        <span>Dear ThreeFold Token Holder,
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see <a href="https://wiki.threefold.io/#/threefold_councils.md" target="_blank">https://wiki.threefold.io/#/threefold_councils.md</a>
    +
    +        Only the end results will be visible by the general community which is:
    +        - The voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.</span>
    +        """
    +
    +        self.md_show(dedent(statement_1), md=True)
    +
    +        default_answer = self.get_question_answer("full_name")
    +        full_name = self.string_ask("What is your full name ?", required=True, default=default_answer)
    +        self.extra_data.update({"full_name": full_name})
    +
    +        statement_2 = f"""<span>Please read the decentralization manifesto v{MANIFESTO_VERSION} on <a href="http://decentralization2.threefold.io" target="_blank">http://decentralization2.threefold.io</a></span>"""
    +        self.md_show(dedent(statement_2), md=True)
    +
    +    def custom_votes(self):
    +        super().custom_votes()
    +
    +        default_answer = self.get_vote_answer(VOTES[1]["title"])
    +        vote_1_answer = self.single_choice(
    +            VOTES[1]["content"].strip(), VOTES[1]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +        default_answer = self.get_vote_answer(VOTES[2]["title"])
    +        vote_2_answer = self.single_choice(
    +            VOTES[2]["content"].strip(), VOTES[2]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +        if vote_2_answer == VOTES[2]["options"][1]:
    +            self.md_show(
    +                f"You did not agree with the decentralization manifesto v{MANIFESTO_VERSION}. Thank you for your participation."
    +            )
    +        else:
    +            self.md_show(
    +                f'Thank you for confirming our "Decentralization manifesto v{MANIFESTO_VERSION}", you have now digitally signed this document.'
    +            )
    +
    +        default_answer = self.get_vote_answer(VOTES[3]["title"])
    +        vote_3_answer = self.single_choice(
    +            VOTES[3]["content"].strip(), VOTES[3]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +        default_answer = self.get_vote_answer(VOTES[4]["title"])
    +        vote_4_answer = self.single_choice(
    +            VOTES[4]["content"].strip(), VOTES[4]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +        self.vote()
    +
    +
    +chat = TFPoll
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class TFPoll +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class TFPoll(Poll):
    +    poll_name = "threefold"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.extra_data = {}
    +        self.custom_answers = {}
    +        self.metadata = {"new_title_keys": NEW_TITLE_KEYS}
    +        self.QUESTIONS = {vote["title"]: vote["options"] for vote in VOTES.values()}
    +
    +    def welcome(self):
    +        stored_extra_data = self.user.extra_data
    +
    +        statement_1 = """\
    +        <span>Dear ThreeFold Token Holder,
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see <a href="https://wiki.threefold.io/#/threefold_councils.md" target="_blank">https://wiki.threefold.io/#/threefold_councils.md</a>
    +
    +        Only the end results will be visible by the general community which is:
    +        - The voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.</span>
    +        """
    +
    +        self.md_show(dedent(statement_1), md=True)
    +
    +        default_answer = self.get_question_answer("full_name")
    +        full_name = self.string_ask("What is your full name ?", required=True, default=default_answer)
    +        self.extra_data.update({"full_name": full_name})
    +
    +        statement_2 = f"""<span>Please read the decentralization manifesto v{MANIFESTO_VERSION} on <a href="http://decentralization2.threefold.io" target="_blank">http://decentralization2.threefold.io</a></span>"""
    +        self.md_show(dedent(statement_2), md=True)
    +
    +    def custom_votes(self):
    +        super().custom_votes()
    +
    +        default_answer = self.get_vote_answer(VOTES[1]["title"])
    +        vote_1_answer = self.single_choice(
    +            VOTES[1]["content"].strip(), VOTES[1]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +        default_answer = self.get_vote_answer(VOTES[2]["title"])
    +        vote_2_answer = self.single_choice(
    +            VOTES[2]["content"].strip(), VOTES[2]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +        if vote_2_answer == VOTES[2]["options"][1]:
    +            self.md_show(
    +                f"You did not agree with the decentralization manifesto v{MANIFESTO_VERSION}. Thank you for your participation."
    +            )
    +        else:
    +            self.md_show(
    +                f'Thank you for confirming our "Decentralization manifesto v{MANIFESTO_VERSION}", you have now digitally signed this document.'
    +            )
    +
    +        default_answer = self.get_vote_answer(VOTES[3]["title"])
    +        vote_3_answer = self.single_choice(
    +            VOTES[3]["content"].strip(), VOTES[3]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +        default_answer = self.get_vote_answer(VOTES[4]["title"])
    +        vote_4_answer = self.single_choice(
    +            VOTES[4]["content"].strip(), VOTES[4]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +        self.vote()
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var poll_name
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def custom_votes(self) +
    +
    +
    +
    + +Expand source code + +
    def custom_votes(self):
    +    super().custom_votes()
    +
    +    default_answer = self.get_vote_answer(VOTES[1]["title"])
    +    vote_1_answer = self.single_choice(
    +        VOTES[1]["content"].strip(), VOTES[1]["options"], default=default_answer, md=True, required=True
    +    )
    +    self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +    default_answer = self.get_vote_answer(VOTES[2]["title"])
    +    vote_2_answer = self.single_choice(
    +        VOTES[2]["content"].strip(), VOTES[2]["options"], default=default_answer, md=True, required=True
    +    )
    +    self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +    if vote_2_answer == VOTES[2]["options"][1]:
    +        self.md_show(
    +            f"You did not agree with the decentralization manifesto v{MANIFESTO_VERSION}. Thank you for your participation."
    +        )
    +    else:
    +        self.md_show(
    +            f'Thank you for confirming our "Decentralization manifesto v{MANIFESTO_VERSION}", you have now digitally signed this document.'
    +        )
    +
    +    default_answer = self.get_vote_answer(VOTES[3]["title"])
    +    vote_3_answer = self.single_choice(
    +        VOTES[3]["content"].strip(), VOTES[3]["options"], default=default_answer, md=True, required=True
    +    )
    +    self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +    default_answer = self.get_vote_answer(VOTES[4]["title"])
    +    vote_4_answer = self.single_choice(
    +        VOTES[4]["content"].strip(), VOTES[4]["options"], default=default_answer, md=True, required=True
    +    )
    +    self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +    self.vote()
    +
    +
    +
    +def welcome(self) +
    +
    +
    +
    + +Expand source code + +
    def welcome(self):
    +    stored_extra_data = self.user.extra_data
    +
    +    statement_1 = """\
    +    <span>Dear ThreeFold Token Holder,
    +
    +    This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid).
    +
    +    This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +    The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see <a href="https://wiki.threefold.io/#/threefold_councils.md" target="_blank">https://wiki.threefold.io/#/threefold_councils.md</a>
    +
    +    Only the end results will be visible by the general community which is:
    +    - The voting questions (comes at end of this poll)
    +    - % of votes as results per question, weighted and unweighted
    +    - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.</span>
    +    """
    +
    +    self.md_show(dedent(statement_1), md=True)
    +
    +    default_answer = self.get_question_answer("full_name")
    +    full_name = self.string_ask("What is your full name ?", required=True, default=default_answer)
    +    self.extra_data.update({"full_name": full_name})
    +
    +    statement_2 = f"""<span>Please read the decentralization manifesto v{MANIFESTO_VERSION} on <a href="http://decentralization2.threefold.io" target="_blank">http://decentralization2.threefold.io</a></span>"""
    +    self.md_show(dedent(statement_2), md=True)
    +
    +
    +
    +
    +
    +class chat +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class TFPoll(Poll):
    +    poll_name = "threefold"
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.extra_data = {}
    +        self.custom_answers = {}
    +        self.metadata = {"new_title_keys": NEW_TITLE_KEYS}
    +        self.QUESTIONS = {vote["title"]: vote["options"] for vote in VOTES.values()}
    +
    +    def welcome(self):
    +        stored_extra_data = self.user.extra_data
    +
    +        statement_1 = """\
    +        <span>Dear ThreeFold Token Holder,
    +
    +        This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid).
    +
    +        This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +        The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see <a href="https://wiki.threefold.io/#/threefold_councils.md" target="_blank">https://wiki.threefold.io/#/threefold_councils.md</a>
    +
    +        Only the end results will be visible by the general community which is:
    +        - The voting questions (comes at end of this poll)
    +        - % of votes as results per question, weighted and unweighted
    +        - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.</span>
    +        """
    +
    +        self.md_show(dedent(statement_1), md=True)
    +
    +        default_answer = self.get_question_answer("full_name")
    +        full_name = self.string_ask("What is your full name ?", required=True, default=default_answer)
    +        self.extra_data.update({"full_name": full_name})
    +
    +        statement_2 = f"""<span>Please read the decentralization manifesto v{MANIFESTO_VERSION} on <a href="http://decentralization2.threefold.io" target="_blank">http://decentralization2.threefold.io</a></span>"""
    +        self.md_show(dedent(statement_2), md=True)
    +
    +    def custom_votes(self):
    +        super().custom_votes()
    +
    +        default_answer = self.get_vote_answer(VOTES[1]["title"])
    +        vote_1_answer = self.single_choice(
    +            VOTES[1]["content"].strip(), VOTES[1]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +        default_answer = self.get_vote_answer(VOTES[2]["title"])
    +        vote_2_answer = self.single_choice(
    +            VOTES[2]["content"].strip(), VOTES[2]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +        if vote_2_answer == VOTES[2]["options"][1]:
    +            self.md_show(
    +                f"You did not agree with the decentralization manifesto v{MANIFESTO_VERSION}. Thank you for your participation."
    +            )
    +        else:
    +            self.md_show(
    +                f'Thank you for confirming our "Decentralization manifesto v{MANIFESTO_VERSION}", you have now digitally signed this document.'
    +            )
    +
    +        default_answer = self.get_vote_answer(VOTES[3]["title"])
    +        vote_3_answer = self.single_choice(
    +            VOTES[3]["content"].strip(), VOTES[3]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +        default_answer = self.get_vote_answer(VOTES[4]["title"])
    +        vote_4_answer = self.single_choice(
    +            VOTES[4]["content"].strip(), VOTES[4]["options"], default=default_answer, md=True, required=True
    +        )
    +        self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +        self.vote()
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var poll_name
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def custom_votes(self) +
    +
    +
    +
    + +Expand source code + +
    def custom_votes(self):
    +    super().custom_votes()
    +
    +    default_answer = self.get_vote_answer(VOTES[1]["title"])
    +    vote_1_answer = self.single_choice(
    +        VOTES[1]["content"].strip(), VOTES[1]["options"], default=default_answer, md=True, required=True
    +    )
    +    self.custom_answers.update({VOTES[1]["title"]: vote_1_answer})
    +
    +    default_answer = self.get_vote_answer(VOTES[2]["title"])
    +    vote_2_answer = self.single_choice(
    +        VOTES[2]["content"].strip(), VOTES[2]["options"], default=default_answer, md=True, required=True
    +    )
    +    self.custom_answers.update({VOTES[2]["title"]: vote_2_answer})
    +
    +    if vote_2_answer == VOTES[2]["options"][1]:
    +        self.md_show(
    +            f"You did not agree with the decentralization manifesto v{MANIFESTO_VERSION}. Thank you for your participation."
    +        )
    +    else:
    +        self.md_show(
    +            f'Thank you for confirming our "Decentralization manifesto v{MANIFESTO_VERSION}", you have now digitally signed this document.'
    +        )
    +
    +    default_answer = self.get_vote_answer(VOTES[3]["title"])
    +    vote_3_answer = self.single_choice(
    +        VOTES[3]["content"].strip(), VOTES[3]["options"], default=default_answer, md=True, required=True
    +    )
    +    self.custom_answers.update({VOTES[3]["title"]: vote_3_answer})
    +
    +    default_answer = self.get_vote_answer(VOTES[4]["title"])
    +    vote_4_answer = self.single_choice(
    +        VOTES[4]["content"].strip(), VOTES[4]["options"], default=default_answer, md=True, required=True
    +    )
    +    self.custom_answers.update({VOTES[4]["title"]: vote_4_answer})
    +
    +    self.vote()
    +
    +
    +
    +def welcome(self) +
    +
    +
    +
    + +Expand source code + +
    def welcome(self):
    +    stored_extra_data = self.user.extra_data
    +
    +    statement_1 = """\
    +    <span>Dear ThreeFold Token Holder,
    +
    +    This is the first poll organized by the foundation using our newly developed ThreeFold voting system. Your votes at the end of this wizard are super important to the future of the ThreeFold Grid (TF Grid).
    +
    +    This first poll is related to introducing a new era in the ThreeFold Grid which leads to even more decentralization and it is important to have your support.
    +
    +    The detailed poll results will only be visible & consulted by the members of the TFgrid Council: see <a href="https://wiki.threefold.io/#/threefold_councils.md" target="_blank">https://wiki.threefold.io/#/threefold_councils.md</a>
    +
    +    Only the end results will be visible by the general community which is:
    +    - The voting questions (comes at end of this poll)
    +    - % of votes as results per question, weighted and unweighted
    +    - Unweighted means: each vote = 1, weighted means each vote in relation to nr of tokens the vote represents.</span>
    +    """
    +
    +    self.md_show(dedent(statement_1), md=True)
    +
    +    default_answer = self.get_question_answer("full_name")
    +    full_name = self.string_ask("What is your full name ?", required=True, default=default_answer)
    +    self.extra_data.update({"full_name": full_name})
    +
    +    statement_2 = f"""<span>Please read the decentralization manifesto v{MANIFESTO_VERSION} on <a href="http://decentralization2.threefold.io" target="_blank">http://decentralization2.threefold.io</a></span>"""
    +    self.md_show(dedent(statement_2), md=True)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/index.html b/docs/api/jumpscale/packages/polls/index.html new file mode 100644 index 000000000..f32a9b8f1 --- /dev/null +++ b/docs/api/jumpscale/packages/polls/index.html @@ -0,0 +1,144 @@ + + + + + + +jumpscale.packages.polls API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.polls

    +
    +
    +

    Polls Package

    +

    Installation

    +
      +
    • +

      Configure a wallet using stellar client named "polls_receive" and update the trustlines see: Quick Start

      +
    • +
    • +

      Note: if you want to deploy test instance make sure the wallet's network is "TEST", for production use "STD"

      +
    • +
    • +

      Note: Make sure to activate the wallet and update trust lines

      +
    • +
    • +

      and its name tp polls.py +python +WALLET_NAME = "polls_receive"

      +
    • +
    • +

      Start 3Bot server and add the package

      +
    • +
    • +

      polls now available at >

      +
    • +
    +

    Create new poll

    +
      +
    • +

      check one of the examples

      +
    • +
    • +

      Default questions will be added in self.QUESTIONS variable

      +
    • +
    • custom vote allows you to add your custom questions just update the dicts and return of it
    • +
    +
    + +Expand source code + +
    """Polls Package
    +
    +## Installation
    +
    +- Configure a wallet using stellar client named "polls_receive" and update the trustlines see: [Quick Start](https://github.com/threefoldtech/js-sdk/blob/9411bf6a359c88994475d4f90c412c1112d3487d/docs/wiki/quick_start.md#L77)
    +
    +- Note: if you want to deploy test instance make sure the wallet's network is "TEST", for production use "STD"
    +- Note: Make sure to activate the wallet and update trust lines
    +
    +- and its name tp polls.py
    +  ```python
    +  WALLET_NAME = "polls_receive"
    +  ```
    +
    +- Start 3Bot server and add the package
    +
    +- polls now available at http://localhost/polls/chats/<poll_name>
    +
    +
    +## Create new poll
    +
    +- check one of the examples
    +
    +  - Default questions will be added in self.QUESTIONS variable
    +  - custom vote allows you to add your custom questions just update the dicts and return of it
    +
    +"""
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.packages.polls.bottle
    +
    +

    Bottle server for tf council …

    +
    +
    jumpscale.packages.polls.chats
    +
    +
    +
    +
    jumpscale.packages.polls.package
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/polls/package.html b/docs/api/jumpscale/packages/polls/package.html new file mode 100644 index 000000000..d4280eeed --- /dev/null +++ b/docs/api/jumpscale/packages/polls/package.html @@ -0,0 +1,122 @@ + + + + + + +jumpscale.packages.polls.package API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.polls.package

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +
    +
    +class polls:
    +    def install(self, **kwargs):
    +        if "polls_receive" not in j.clients.stellar.list_all():
    +            secret = kwargs.get("secret", None)
    +            wallet = j.clients.stellar.new("polls_receive", secret=secret)
    +            if not secret:
    +                wallet.activate_through_threefold_service()
    +            wallet.save()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class polls +
    +
    +
    +
    + +Expand source code + +
    class polls:
    +    def install(self, **kwargs):
    +        if "polls_receive" not in j.clients.stellar.list_all():
    +            secret = kwargs.get("secret", None)
    +            wallet = j.clients.stellar.new("polls_receive", secret=secret)
    +            if not secret:
    +                wallet.activate_through_threefold_service()
    +            wallet.save()
    +
    +

    Methods

    +
    +
    +def install(self, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def install(self, **kwargs):
    +    if "polls_receive" not in j.clients.stellar.list_all():
    +        secret = kwargs.get("secret", None)
    +        wallet = j.clients.stellar.new("polls_receive", secret=secret)
    +        if not secret:
    +            wallet.activate_through_threefold_service()
    +        wallet.save()
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/stellar_stats/bottle/index.html b/docs/api/jumpscale/packages/stellar_stats/bottle/index.html new file mode 100644 index 000000000..a6a59c400 --- /dev/null +++ b/docs/api/jumpscale/packages/stellar_stats/bottle/index.html @@ -0,0 +1,70 @@ + + + + + + +jumpscale.packages.stellar_stats.bottle API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/stellar_stats/bottle/stats_service.html b/docs/api/jumpscale/packages/stellar_stats/bottle/stats_service.html new file mode 100644 index 000000000..2bb3d4b93 --- /dev/null +++ b/docs/api/jumpscale/packages/stellar_stats/bottle/stats_service.html @@ -0,0 +1,477 @@ + + + + + + +jumpscale.packages.stellar_stats.bottle.stats_service API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.stellar_stats.bottle.stats_service

    +
    +
    +
    + +Expand source code + +
    #!/usr/bin/env python
    +# pylint: disable=no-value-for-parameter
    +
    +from jumpscale.loader import j
    +import click
    +import stellar_sdk
    +import datetime
    +
    +from urllib import parse
    +
    +
    +_ASSET_ISUERS = {
    +    "test": {
    +        "TFT": "GA47YZA3PKFUZMPLQ3B5F2E3CJIB57TGGU7SPCQT2WAEYKN766PWIMB3",
    +        "FreeTFT": "GBLDUINEFYTF7XEE7YNWA3JQS4K2VD37YU7I2YAE7R5AHZDKQXSS2J6R",
    +        "TFTA": "GB55A4RR4G2MIORJTQA4L6FENZU7K4W7ATGY6YOT2CW47M5SZYGYKSCT",
    +    },
    +    "public": {
    +        "TFT": "GBOVQKJYHXRR3DX6NOX2RRYFRCUMSADGDESTDNBDS6CDVLGVESRTAC47",
    +        "FreeTFT": "GCBGS5TFE2BPPUVY55ZPEMWWGR6CLQ7T6P46SOFGHXEBJ34MSP6HVEUT",
    +        "TFTA": "GBUT4GP5GJ6B3XW5PXENHQA7TXJI5GOPW3NF4W3ZIW6OOO4ISY6WNLN2",
    +    },
    +}
    +_HORIZON_NETWORKS = {"test": "https://horizon-testnet.stellar.org", "public": "https://horizon.stellar.org"}
    +_THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES = {"test": "testnet.threefold.io", "public": "tokenservices.threefold.io"}
    +
    +_NETWORK_PASSPHRASES = {
    +    "test": stellar_sdk.Network.TESTNET_NETWORK_PASSPHRASE,
    +    "public": stellar_sdk.Network.PUBLIC_NETWORK_PASSPHRASE,
    +}
    +
    +
    +class StatisticsCollector(object):
    +    """
    +    Gathers statistics on TFT, TFTA and FreeTFT.
    +    Reuse an instance of this class to cache the locktimes of escrow accounts and speed up the detailed statistics gathering
    +    """
    +
    +    def __init__(self, network: str):
    +        self._network = network
    +        self.locked_accounts = {}
    +
    +    @property
    +    def _horizon_server(self):
    +        server_url = _HORIZON_NETWORKS[self._network]
    +        return stellar_sdk.Server(horizon_url=server_url)
    +
    +    @property
    +    def _network_passphrase(self):
    +        return _NETWORK_PASSPHRASES[self._network]
    +
    +    def get_accounts(self, tokencode: str, issuer: str):
    +        locked_accounts = []
    +        horizon_server = self._horizon_server
    +        asset = stellar_sdk.Asset(tokencode, issuer)
    +        accounts_endpoint = horizon_server.accounts().for_asset(asset).limit(50)
    +        old_cursor = "old"
    +        new_cursor = ""
    +        while new_cursor != old_cursor:
    +            old_cursor = new_cursor
    +            accounts_endpoint.cursor(new_cursor)
    +            response = accounts_endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +            accounts = response["_embedded"]["records"]
    +            for account in accounts:
    +                account_id = account["account_id"]
    +                preauth_signers = [signer["key"] for signer in account["signers"] if signer["type"] == "preauth_tx"]
    +                tokenbalances = [
    +                    float(b["balance"])
    +                    for b in account["balances"]
    +                    if b["asset_type"] == "credit_alphanum4"
    +                    and b["asset_code"] == tokencode
    +                    and b["asset_issuer"] == issuer
    +                ]
    +                tokenbalance = tokenbalances[0] if tokenbalances else 0
    +                if len(preauth_signers) > 0:
    +                    locked_accounts.append(
    +                        {"account": account_id, "amount": tokenbalance, "preauth_signers": preauth_signers}
    +                    )
    +        return locked_accounts
    +
    +    def _get_url(self, endpoint):
    +        url = _THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[self._network]
    +        if not j.sals.nettools.wait_connection_test(url, 443, 5):
    +            raise j.exceptions.Timeout(f"Can not connect to server {url}, connection timeout")
    +        return f"https://{url}{endpoint}"
    +
    +    def _get_unlockhash_transaction(self, unlockhash):
    +        data = {"unlockhash": unlockhash}
    +        resp = j.tools.http.post(
    +            self._get_url("/threefoldfoundation/unlock_service/get_unlockhash_transaction"), json={"args": data}
    +        )
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def lookup_lock_time(self, preauth_signer: str):
    +        unlock_tx = self._get_unlockhash_transaction(unlockhash=preauth_signer)
    +        if unlock_tx is None:
    +            return None
    +        txe = stellar_sdk.TransactionEnvelope.from_xdr(unlock_tx["transaction_xdr"], self._network_passphrase)
    +        tx = txe.transaction
    +        if tx.time_bounds is not None:
    +            return tx.time_bounds.min_time
    +        return None
    +
    +    def getstatistics(self, tokencode: str, detailed: bool):
    +        stats = {"asset": tokencode}
    +        horizon_server = self._horizon_server
    +        asset_issuer = _ASSET_ISUERS[self._network][tokencode]
    +        stats["issuer"] = asset_issuer
    +        response = horizon_server.assets().for_code(tokencode).for_issuer(asset_issuer).call()
    +        record = response["_embedded"]["records"][0]
    +        stats["total"] = float(record["amount"])
    +        stats["num_accounts"] = record["num_accounts"]
    +        locked_accounts = self.get_accounts(tokencode, asset_issuer)
    +        total_locked = 0.0
    +        for locked_account in locked_accounts:
    +            total_locked += locked_account["amount"]
    +        stats["total_locked"] = total_locked
    +        if not detailed:
    +            return stats
    +        amounts_per_locktime = {}
    +        for locked_account in locked_accounts:
    +            if locked_account["account"] in self.locked_accounts:
    +                locked_account["until"] = self.locked_accounts[locked_account["account"]]["until"]
    +            else:
    +                locked_account["until"] = self.lookup_lock_time(locked_account["preauth_signers"][0])
    +                self.locked_accounts[locked_account["account"]] = locked_account
    +        for locked_account in locked_accounts:
    +            amount = amounts_per_locktime.get(locked_account["until"], 0.0)
    +            amount += locked_account["amount"]
    +            amounts_per_locktime[locked_account["until"]] = amount
    +        locked_amounts = []
    +        for until, amount in amounts_per_locktime.items():
    +            locked_amounts.append({"until": until, "amount": amount})
    +
    +        def sort_key(a):
    +            return a["until"]
    +
    +        locked_amounts.sort(key=sort_key)
    +        stats["locked"] = locked_amounts
    +        return stats
    +
    +
    +@click.command(help="Statistics about TFT, TFTA and FreeTFT")
    +@click.argument("tokencode", type=click.Choice(["TFT", "TFTA", "FreeTFT"]), default="TFT")
    +@click.option("--network", type=click.Choice(["test", "public"], case_sensitive=False), default="public")
    +@click.option("--detailed/--not-detailed", default=False)
    +def show_stats(tokencode, network, detailed):
    +    print(f"Statistics for {tokencode} on the {network} network")
    +    collector = StatisticsCollector(network)
    +    stats = collector.getstatistics(tokencode, detailed)
    +    print(f"Total amount of tokens: {stats['total']:,.7f}")
    +    print(f"Number of accounts: {stats['num_accounts']}")
    +    print(f"Amount of locked tokens: {stats['total_locked']:,.7f}")
    +    if detailed:
    +        for locked_amount in stats["locked"]:
    +            print(
    +                f"{locked_amount['amount']:,.7f} locked until {datetime.datetime.fromtimestamp(locked_amount['until'])}"
    +            )
    +
    +
    +if __name__ == "__main__":
    +    show_stats()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class StatisticsCollector +(network: str) +
    +
    +

    Gathers statistics on TFT, TFTA and FreeTFT. +Reuse an instance of this class to cache the locktimes of escrow accounts and speed up the detailed statistics gathering

    +
    + +Expand source code + +
    class StatisticsCollector(object):
    +    """
    +    Gathers statistics on TFT, TFTA and FreeTFT.
    +    Reuse an instance of this class to cache the locktimes of escrow accounts and speed up the detailed statistics gathering
    +    """
    +
    +    def __init__(self, network: str):
    +        self._network = network
    +        self.locked_accounts = {}
    +
    +    @property
    +    def _horizon_server(self):
    +        server_url = _HORIZON_NETWORKS[self._network]
    +        return stellar_sdk.Server(horizon_url=server_url)
    +
    +    @property
    +    def _network_passphrase(self):
    +        return _NETWORK_PASSPHRASES[self._network]
    +
    +    def get_accounts(self, tokencode: str, issuer: str):
    +        locked_accounts = []
    +        horizon_server = self._horizon_server
    +        asset = stellar_sdk.Asset(tokencode, issuer)
    +        accounts_endpoint = horizon_server.accounts().for_asset(asset).limit(50)
    +        old_cursor = "old"
    +        new_cursor = ""
    +        while new_cursor != old_cursor:
    +            old_cursor = new_cursor
    +            accounts_endpoint.cursor(new_cursor)
    +            response = accounts_endpoint.call()
    +            next_link = response["_links"]["next"]["href"]
    +            next_link_query = parse.urlsplit(next_link).query
    +            new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +            accounts = response["_embedded"]["records"]
    +            for account in accounts:
    +                account_id = account["account_id"]
    +                preauth_signers = [signer["key"] for signer in account["signers"] if signer["type"] == "preauth_tx"]
    +                tokenbalances = [
    +                    float(b["balance"])
    +                    for b in account["balances"]
    +                    if b["asset_type"] == "credit_alphanum4"
    +                    and b["asset_code"] == tokencode
    +                    and b["asset_issuer"] == issuer
    +                ]
    +                tokenbalance = tokenbalances[0] if tokenbalances else 0
    +                if len(preauth_signers) > 0:
    +                    locked_accounts.append(
    +                        {"account": account_id, "amount": tokenbalance, "preauth_signers": preauth_signers}
    +                    )
    +        return locked_accounts
    +
    +    def _get_url(self, endpoint):
    +        url = _THREEFOLDFOUNDATION_TFTSTELLAR_SERVICES[self._network]
    +        if not j.sals.nettools.wait_connection_test(url, 443, 5):
    +            raise j.exceptions.Timeout(f"Can not connect to server {url}, connection timeout")
    +        return f"https://{url}{endpoint}"
    +
    +    def _get_unlockhash_transaction(self, unlockhash):
    +        data = {"unlockhash": unlockhash}
    +        resp = j.tools.http.post(
    +            self._get_url("/threefoldfoundation/unlock_service/get_unlockhash_transaction"), json={"args": data}
    +        )
    +        resp.raise_for_status()
    +        return resp.json()
    +
    +    def lookup_lock_time(self, preauth_signer: str):
    +        unlock_tx = self._get_unlockhash_transaction(unlockhash=preauth_signer)
    +        if unlock_tx is None:
    +            return None
    +        txe = stellar_sdk.TransactionEnvelope.from_xdr(unlock_tx["transaction_xdr"], self._network_passphrase)
    +        tx = txe.transaction
    +        if tx.time_bounds is not None:
    +            return tx.time_bounds.min_time
    +        return None
    +
    +    def getstatistics(self, tokencode: str, detailed: bool):
    +        stats = {"asset": tokencode}
    +        horizon_server = self._horizon_server
    +        asset_issuer = _ASSET_ISUERS[self._network][tokencode]
    +        stats["issuer"] = asset_issuer
    +        response = horizon_server.assets().for_code(tokencode).for_issuer(asset_issuer).call()
    +        record = response["_embedded"]["records"][0]
    +        stats["total"] = float(record["amount"])
    +        stats["num_accounts"] = record["num_accounts"]
    +        locked_accounts = self.get_accounts(tokencode, asset_issuer)
    +        total_locked = 0.0
    +        for locked_account in locked_accounts:
    +            total_locked += locked_account["amount"]
    +        stats["total_locked"] = total_locked
    +        if not detailed:
    +            return stats
    +        amounts_per_locktime = {}
    +        for locked_account in locked_accounts:
    +            if locked_account["account"] in self.locked_accounts:
    +                locked_account["until"] = self.locked_accounts[locked_account["account"]]["until"]
    +            else:
    +                locked_account["until"] = self.lookup_lock_time(locked_account["preauth_signers"][0])
    +                self.locked_accounts[locked_account["account"]] = locked_account
    +        for locked_account in locked_accounts:
    +            amount = amounts_per_locktime.get(locked_account["until"], 0.0)
    +            amount += locked_account["amount"]
    +            amounts_per_locktime[locked_account["until"]] = amount
    +        locked_amounts = []
    +        for until, amount in amounts_per_locktime.items():
    +            locked_amounts.append({"until": until, "amount": amount})
    +
    +        def sort_key(a):
    +            return a["until"]
    +
    +        locked_amounts.sort(key=sort_key)
    +        stats["locked"] = locked_amounts
    +        return stats
    +
    +

    Methods

    +
    +
    +def get_accounts(self, tokencode: str, issuer: str) +
    +
    +
    +
    + +Expand source code + +
    def get_accounts(self, tokencode: str, issuer: str):
    +    locked_accounts = []
    +    horizon_server = self._horizon_server
    +    asset = stellar_sdk.Asset(tokencode, issuer)
    +    accounts_endpoint = horizon_server.accounts().for_asset(asset).limit(50)
    +    old_cursor = "old"
    +    new_cursor = ""
    +    while new_cursor != old_cursor:
    +        old_cursor = new_cursor
    +        accounts_endpoint.cursor(new_cursor)
    +        response = accounts_endpoint.call()
    +        next_link = response["_links"]["next"]["href"]
    +        next_link_query = parse.urlsplit(next_link).query
    +        new_cursor = parse.parse_qs(next_link_query)["cursor"][0]
    +        accounts = response["_embedded"]["records"]
    +        for account in accounts:
    +            account_id = account["account_id"]
    +            preauth_signers = [signer["key"] for signer in account["signers"] if signer["type"] == "preauth_tx"]
    +            tokenbalances = [
    +                float(b["balance"])
    +                for b in account["balances"]
    +                if b["asset_type"] == "credit_alphanum4"
    +                and b["asset_code"] == tokencode
    +                and b["asset_issuer"] == issuer
    +            ]
    +            tokenbalance = tokenbalances[0] if tokenbalances else 0
    +            if len(preauth_signers) > 0:
    +                locked_accounts.append(
    +                    {"account": account_id, "amount": tokenbalance, "preauth_signers": preauth_signers}
    +                )
    +    return locked_accounts
    +
    +
    +
    +def getstatistics(self, tokencode: str, detailed: bool) +
    +
    +
    +
    + +Expand source code + +
    def getstatistics(self, tokencode: str, detailed: bool):
    +    stats = {"asset": tokencode}
    +    horizon_server = self._horizon_server
    +    asset_issuer = _ASSET_ISUERS[self._network][tokencode]
    +    stats["issuer"] = asset_issuer
    +    response = horizon_server.assets().for_code(tokencode).for_issuer(asset_issuer).call()
    +    record = response["_embedded"]["records"][0]
    +    stats["total"] = float(record["amount"])
    +    stats["num_accounts"] = record["num_accounts"]
    +    locked_accounts = self.get_accounts(tokencode, asset_issuer)
    +    total_locked = 0.0
    +    for locked_account in locked_accounts:
    +        total_locked += locked_account["amount"]
    +    stats["total_locked"] = total_locked
    +    if not detailed:
    +        return stats
    +    amounts_per_locktime = {}
    +    for locked_account in locked_accounts:
    +        if locked_account["account"] in self.locked_accounts:
    +            locked_account["until"] = self.locked_accounts[locked_account["account"]]["until"]
    +        else:
    +            locked_account["until"] = self.lookup_lock_time(locked_account["preauth_signers"][0])
    +            self.locked_accounts[locked_account["account"]] = locked_account
    +    for locked_account in locked_accounts:
    +        amount = amounts_per_locktime.get(locked_account["until"], 0.0)
    +        amount += locked_account["amount"]
    +        amounts_per_locktime[locked_account["until"]] = amount
    +    locked_amounts = []
    +    for until, amount in amounts_per_locktime.items():
    +        locked_amounts.append({"until": until, "amount": amount})
    +
    +    def sort_key(a):
    +        return a["until"]
    +
    +    locked_amounts.sort(key=sort_key)
    +    stats["locked"] = locked_amounts
    +    return stats
    +
    +
    +
    +def lookup_lock_time(self, preauth_signer: str) +
    +
    +
    +
    + +Expand source code + +
    def lookup_lock_time(self, preauth_signer: str):
    +    unlock_tx = self._get_unlockhash_transaction(unlockhash=preauth_signer)
    +    if unlock_tx is None:
    +        return None
    +    txe = stellar_sdk.TransactionEnvelope.from_xdr(unlock_tx["transaction_xdr"], self._network_passphrase)
    +    tx = txe.transaction
    +    if tx.time_bounds is not None:
    +        return tx.time_bounds.min_time
    +    return None
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/stellar_stats/bottle/stellar_stats.html b/docs/api/jumpscale/packages/stellar_stats/bottle/stellar_stats.html new file mode 100644 index 000000000..f7cc63e1f --- /dev/null +++ b/docs/api/jumpscale/packages/stellar_stats/bottle/stellar_stats.html @@ -0,0 +1,272 @@ + + + + + + +jumpscale.packages.stellar_stats.bottle.stellar_stats API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.stellar_stats.bottle.stellar_stats

    +
    +
    +
    + +Expand source code + +
    import datetime
    +
    +from bottle import Bottle, request
    +
    +from jumpscale.loader import j
    +from jumpscale.packages.stellar_stats.bottle.stats_service import StatisticsCollector
    +
    +app = Bottle()
    +
    +
    +@app.route("/api/stats")
    +def get_stats():
    +    """Statistics about TFT, TFTA and FreeTFT
    +
    +    Args:
    +        network (str ["test", "public"], optional): Defaults to "public".
    +        tokencode (str ["TFT", "TFTA", "FreeTFT"], optional): Defaults to "TFT".
    +        detailed (bool, optional): Defaults to False.
    +    """
    +    query_params = request.query.decode()
    +    network = query_params.get("network", "public")
    +    tokencode = query_params.get("tokencode", "TFT")
    +    detailed = j.data.serializers.json.loads(query_params.get("detailed", "false"))
    +    res = {}
    +
    +    # cache the request in local redis
    +    redis = j.clients.redis.get("redis_instance")
    +    cached_data = redis.get(f"{network}-{tokencode}-{detailed}")
    +    if cached_data:
    +        return cached_data
    +
    +    collector = StatisticsCollector(network)
    +    stats = collector.getstatistics(tokencode, detailed)
    +    res["total_tokens"] = f"{stats['total']:,.7f}"
    +    res["total_accounts"] = f"{stats['num_accounts']}"
    +    res["total_locked_tokens"] = f"{stats['total_locked']:,.7f}"
    +    if detailed:
    +        res["locked_tokens_info"] = []
    +        for locked_amount in stats["locked"]:
    +            res["locked_tokens_info"].append(
    +                f"{locked_amount['amount']:,.7f} locked until {datetime.datetime.fromtimestamp(locked_amount['until'])}"
    +            )
    +    results = j.data.serializers.json.dumps(res)
    +    redis.set(f"{network}-{tokencode}-{detailed}", results, ex=600)
    +
    +    return results
    +
    +
    +@app.route("/api/total_tft")
    +def total_tft():
    +    query_params = request.query.decode()
    +    network = query_params.get("network", "public")
    +    tokencode = query_params.get("tokencode", "TFT")
    +
    +    # cache the request in local redis
    +    redis = j.clients.redis.get("redis_instance")
    +    cached_data = redis.get(f"{network}-{tokencode}-total_tft")
    +    if cached_data:
    +        return cached_data
    +
    +    collector = StatisticsCollector(network)
    +    stats = collector.getstatistics(tokencode, False)
    +
    +    total = stats["total"]
    +    redis.set(f"{network}-{tokencode}-total_tft", total, ex=600)
    +    return f"{total}"
    +
    +
    +@app.route("/api/total_unlocked_tft")
    +def total_unlocked_tft():
    +    query_params = request.query.decode()
    +    network = query_params.get("network", "public")
    +    tokencode = query_params.get("tokencode", "TFT")
    +
    +    # cache the request in local redis
    +    redis = j.clients.redis.get("redis_instance")
    +    cached_data = redis.get(f"{network}-{tokencode}-total_unlocked_tft")
    +    if cached_data:
    +        return cached_data
    +
    +    collector = StatisticsCollector(network)
    +    stats = collector.getstatistics(tokencode, False)
    +
    +    total_tft = stats["total"]
    +    total_locked_tft = stats["total_locked"]
    +    total_unlocked_tft = total_tft - total_locked_tft
    +
    +    redis.set(f"{network}-{tokencode}-total_unlocked_tft", total_unlocked_tft, ex=600)
    +    return f"{total_unlocked_tft}"
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def get_stats() +
    +
    +

    Statistics about TFT, TFTA and FreeTFT

    +

    Args

    +
    +
    network (str ["test", "public"], optional): Defaults to "public".
    +
    tokencode (str ["TFT", "TFTA", "FreeTFT"], optional): Defaults to "TFT".
    +
    detailed : bool, optional
    +
    Defaults to False.
    +
    +
    + +Expand source code + +
    @app.route("/api/stats")
    +def get_stats():
    +    """Statistics about TFT, TFTA and FreeTFT
    +
    +    Args:
    +        network (str ["test", "public"], optional): Defaults to "public".
    +        tokencode (str ["TFT", "TFTA", "FreeTFT"], optional): Defaults to "TFT".
    +        detailed (bool, optional): Defaults to False.
    +    """
    +    query_params = request.query.decode()
    +    network = query_params.get("network", "public")
    +    tokencode = query_params.get("tokencode", "TFT")
    +    detailed = j.data.serializers.json.loads(query_params.get("detailed", "false"))
    +    res = {}
    +
    +    # cache the request in local redis
    +    redis = j.clients.redis.get("redis_instance")
    +    cached_data = redis.get(f"{network}-{tokencode}-{detailed}")
    +    if cached_data:
    +        return cached_data
    +
    +    collector = StatisticsCollector(network)
    +    stats = collector.getstatistics(tokencode, detailed)
    +    res["total_tokens"] = f"{stats['total']:,.7f}"
    +    res["total_accounts"] = f"{stats['num_accounts']}"
    +    res["total_locked_tokens"] = f"{stats['total_locked']:,.7f}"
    +    if detailed:
    +        res["locked_tokens_info"] = []
    +        for locked_amount in stats["locked"]:
    +            res["locked_tokens_info"].append(
    +                f"{locked_amount['amount']:,.7f} locked until {datetime.datetime.fromtimestamp(locked_amount['until'])}"
    +            )
    +    results = j.data.serializers.json.dumps(res)
    +    redis.set(f"{network}-{tokencode}-{detailed}", results, ex=600)
    +
    +    return results
    +
    +
    +
    +def total_tft() +
    +
    +
    +
    + +Expand source code + +
    @app.route("/api/total_tft")
    +def total_tft():
    +    query_params = request.query.decode()
    +    network = query_params.get("network", "public")
    +    tokencode = query_params.get("tokencode", "TFT")
    +
    +    # cache the request in local redis
    +    redis = j.clients.redis.get("redis_instance")
    +    cached_data = redis.get(f"{network}-{tokencode}-total_tft")
    +    if cached_data:
    +        return cached_data
    +
    +    collector = StatisticsCollector(network)
    +    stats = collector.getstatistics(tokencode, False)
    +
    +    total = stats["total"]
    +    redis.set(f"{network}-{tokencode}-total_tft", total, ex=600)
    +    return f"{total}"
    +
    +
    +
    +def total_unlocked_tft() +
    +
    +
    +
    + +Expand source code + +
    @app.route("/api/total_unlocked_tft")
    +def total_unlocked_tft():
    +    query_params = request.query.decode()
    +    network = query_params.get("network", "public")
    +    tokencode = query_params.get("tokencode", "TFT")
    +
    +    # cache the request in local redis
    +    redis = j.clients.redis.get("redis_instance")
    +    cached_data = redis.get(f"{network}-{tokencode}-total_unlocked_tft")
    +    if cached_data:
    +        return cached_data
    +
    +    collector = StatisticsCollector(network)
    +    stats = collector.getstatistics(tokencode, False)
    +
    +    total_tft = stats["total"]
    +    total_locked_tft = stats["total_locked"]
    +    total_unlocked_tft = total_tft - total_locked_tft
    +
    +    redis.set(f"{network}-{tokencode}-total_unlocked_tft", total_unlocked_tft, ex=600)
    +    return f"{total_unlocked_tft}"
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/stellar_stats/index.html b/docs/api/jumpscale/packages/stellar_stats/index.html new file mode 100644 index 000000000..1c629f86f --- /dev/null +++ b/docs/api/jumpscale/packages/stellar_stats/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.stellar_stats API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/weblibs/index.html b/docs/api/jumpscale/packages/weblibs/index.html new file mode 100644 index 000000000..73dda2545 --- /dev/null +++ b/docs/api/jumpscale/packages/weblibs/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.packages.weblibs API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/packages/weblibs/package.html b/docs/api/jumpscale/packages/weblibs/package.html new file mode 100644 index 000000000..87ac765e2 --- /dev/null +++ b/docs/api/jumpscale/packages/weblibs/package.html @@ -0,0 +1,274 @@ + + + + + + +jumpscale.packages.weblibs.package API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.packages.weblibs.package

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +
    +
    +class weblibs:
    +    def __init__(self):
    +        self.url = "https://github.com/threefoldtech/jumpscaleX_weblibs"
    +        self.path = j.sals.fs.join_paths(j.core.dirs.CODEDIR, "github")
    +        self.branch = "development"
    +
    +    def preinstall(self):
    +        """Called once when the package is being installed.
    +
    +        Raises:
    +            e: [description]
    +        """
    +        weblibs_repo_dir = j.sals.fs.join_paths(self.path, "jumpscaleX_weblibs")
    +        if not j.sals.fs.exists(j.sals.fs.join_paths(weblibs_repo_dir, ".git")):
    +            retries = 3
    +            while retries:
    +                try:
    +                    j.tools.git.clone_repo(url=self.url, dest=self.path, branch_or_tag=self.branch, depth=1)
    +                    return
    +                except Exception as e:
    +                    j.sals.fs.rmtree(weblibs_repo_dir)
    +                    retries -= 1
    +                    msg = str(e)
    +                    # check if error not lost internet connection don't try again
    +                    if not retries or msg.find("Could not resolve host") == -1:
    +                        raise e
    +
    +    def install(self):
    +        """Called when package is added
    +        """
    +        pass
    +
    +    def uninstall(self):
    +        """Called when package is deleted
    +        """
    +        pass
    +
    +    def start(self):
    +        """Called when threebot is started
    +        """
    +        pass
    +
    +    def stop(self):
    +        pass
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class weblibs +
    +
    +
    +
    + +Expand source code + +
    class weblibs:
    +    def __init__(self):
    +        self.url = "https://github.com/threefoldtech/jumpscaleX_weblibs"
    +        self.path = j.sals.fs.join_paths(j.core.dirs.CODEDIR, "github")
    +        self.branch = "development"
    +
    +    def preinstall(self):
    +        """Called once when the package is being installed.
    +
    +        Raises:
    +            e: [description]
    +        """
    +        weblibs_repo_dir = j.sals.fs.join_paths(self.path, "jumpscaleX_weblibs")
    +        if not j.sals.fs.exists(j.sals.fs.join_paths(weblibs_repo_dir, ".git")):
    +            retries = 3
    +            while retries:
    +                try:
    +                    j.tools.git.clone_repo(url=self.url, dest=self.path, branch_or_tag=self.branch, depth=1)
    +                    return
    +                except Exception as e:
    +                    j.sals.fs.rmtree(weblibs_repo_dir)
    +                    retries -= 1
    +                    msg = str(e)
    +                    # check if error not lost internet connection don't try again
    +                    if not retries or msg.find("Could not resolve host") == -1:
    +                        raise e
    +
    +    def install(self):
    +        """Called when package is added
    +        """
    +        pass
    +
    +    def uninstall(self):
    +        """Called when package is deleted
    +        """
    +        pass
    +
    +    def start(self):
    +        """Called when threebot is started
    +        """
    +        pass
    +
    +    def stop(self):
    +        pass
    +
    +

    Methods

    +
    +
    +def install(self) +
    +
    +

    Called when package is added

    +
    + +Expand source code + +
    def install(self):
    +    """Called when package is added
    +    """
    +    pass
    +
    +
    +
    +def preinstall(self) +
    +
    +

    Called once when the package is being installed.

    +

    Raises

    +
    +
    e
    +
    [description]
    +
    +
    + +Expand source code + +
    def preinstall(self):
    +    """Called once when the package is being installed.
    +
    +    Raises:
    +        e: [description]
    +    """
    +    weblibs_repo_dir = j.sals.fs.join_paths(self.path, "jumpscaleX_weblibs")
    +    if not j.sals.fs.exists(j.sals.fs.join_paths(weblibs_repo_dir, ".git")):
    +        retries = 3
    +        while retries:
    +            try:
    +                j.tools.git.clone_repo(url=self.url, dest=self.path, branch_or_tag=self.branch, depth=1)
    +                return
    +            except Exception as e:
    +                j.sals.fs.rmtree(weblibs_repo_dir)
    +                retries -= 1
    +                msg = str(e)
    +                # check if error not lost internet connection don't try again
    +                if not retries or msg.find("Could not resolve host") == -1:
    +                    raise e
    +
    +
    +
    +def start(self) +
    +
    +

    Called when threebot is started

    +
    + +Expand source code + +
    def start(self):
    +    """Called when threebot is started
    +    """
    +    pass
    +
    +
    +
    +def stop(self) +
    +
    +
    +
    + +Expand source code + +
    def stop(self):
    +    pass
    +
    +
    +
    +def uninstall(self) +
    +
    +

    Called when package is deleted

    +
    + +Expand source code + +
    def uninstall(self):
    +    """Called when package is deleted
    +    """
    +    pass
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/backupjob/backupjob.html b/docs/api/jumpscale/sals/backupjob/backupjob.html new file mode 100644 index 000000000..57ef847de --- /dev/null +++ b/docs/api/jumpscale/sals/backupjob/backupjob.html @@ -0,0 +1,868 @@ + + + + + + +jumpscale.sals.backupjob.backupjob API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.backupjob.backupjob

    +
    +
    +
    +

    THE BACKUPJOB SAL

    +
    +

    This sal can be used to create and manage multiple backup jobs with multiple and configured paths.

    +

    Examples: +1. create new backup job +- every package could create its backup job when installed with one or multiple paths

    +
    JS-NG> nginxbackup = j.sals.backupjob.new("nginxbackup", clients = ["restic_client_1", "restic_client_2"], paths=["~/sandbox/cfg/nginx/main/"])
    +JS-NG> nginxbackup.save()
    +
    +
      +
    1. +

      create another backup job +JS-NG> vdcbackup = j.sals.backupjob.new("vdcbackup", clients = ["restic_client_3", "restic_client_4"], paths=["~/.config/jumpscale/secureconfig/jumpscale/sals/vdc/"]) +JS-NG> vdcbackup.save()

      +
    2. +
    3. +

      list backup jobs

      +
    4. +
    +
    JS-NG> j.sals.backupjob.list_all()
    +
    +
      +
    1. get and execute a backup job
        +
      • get the backup job +python +JS-NG> nginxbackup_job = j.sals.backupjob.get('nginxbackup') +JS-NG> nginxbackup_job.execute()
      • +
      +
    2. +
    +
    + +Expand source code + +
    """
    +----------------------------------------------------------------------
    +# THE BACKUPJOB SAL
    +----------------------------------------------------------------------
    +This sal can be used to create and manage multiple backup jobs with multiple and configured paths.
    +
    +Examples:
    +1. create new backup job
    +    - every package could create its backup job when installed with one or multiple paths
    +
    +```python
    +JS-NG> nginxbackup = j.sals.backupjob.new("nginxbackup", clients = ["restic_client_1", "restic_client_2"], paths=["~/sandbox/cfg/nginx/main/"])
    +JS-NG> nginxbackup.save()
    +```
    +
    +2. create another backup job
    +JS-NG> vdcbackup = j.sals.backupjob.new("vdcbackup", clients = ["restic_client_3", "restic_client_4"], paths=["~/.config/jumpscale/secureconfig/jumpscale/sals/vdc/"])
    +JS-NG> vdcbackup.save()
    +
    +3. list backup jobs
    +
    +```python
    +JS-NG> j.sals.backupjob.list_all()
    +```
    +
    +4. get and execute a backup job
    +    - get the backup job
    +    ```python
    +    JS-NG> nginxbackup_job = j.sals.backupjob.get('nginxbackup')
    +    JS-NG> nginxbackup_job.execute()
    +    ```
    +"""
    +from gevent.greenlet import Greenlet
    +from jumpscale.loader import j
    +from jumpscale.core.base import Base, fields
    +from jumpscale.sals import fs
    +import gevent
    +
    +
    +def _path_validator(path):
    +    """Raises a ValidationError if a path Neither an absolute path nor begin with a tilde.
    +
    +    Args:
    +        path (str): a path represent a directory/file.
    +
    +    Raises:
    +        j.core.base.fields.ValidationError: If a path Neither an absolute path nor begin with a tilde.
    +    """
    +    if not fs.is_absolute(fs.os.path.expanduser(path)):
    +        raise j.core.base.fields.ValidationError(f"The path {path} should be absolute path or begin with a tilde")
    +
    +
    +def _client_validator(restic_client_name):
    +    """Raises a ValidationError if a restic client instance with given name can not be found.
    +
    +    Args:
    +        restic_client_name (str): a restic client instance name.
    +
    +    Raises:
    +        j.core.base.fields.ValidationError: If a restic client instance with given name can not be found.
    +    """
    +    if restic_client_name not in j.tools.restic.list_all():
    +        raise j.core.base.fields.ValidationError(f"The restic client: {restic_client_name} not found!")
    +
    +
    +class BackupJob(Base):
    +    paths = fields.List(fields.String(validators=[_path_validator]))
    +    paths_to_exclude = fields.List(fields.String())
    +    clients = fields.List(fields.String(validators=[_client_validator]))
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +    @staticmethod
    +    def _get_client(restic_client_name):
    +        """Gets a ResticRepo object with a given instance name.
    +
    +        Args:
    +            restic_client_name (str): Restic instance name.
    +
    +        Raises:
    +            j.exceptions.Runtime: If restic instance not found.
    +
    +        Returns:
    +            ResticRepo: jumpscale.tools.restic.restic.ResticRepo instance.
    +        """
    +        if restic_client_name in j.tools.restic.list_all():
    +            return j.tools.restic.get(restic_client_name)
    +        raise j.exceptions.Runtime(f"The restic client: {restic_client_name} not found!")
    +
    +    def _on_exception(self, g: Greenlet):
    +        client_name = self.clients[self._greenlets.index(g)]
    +        j.logger.exception(
    +            f"BackupJob name: {self.instance_name} - Error happened during Backing up using this ResticRepo: {client_name}",
    +            exception=g.exception,
    +        )
    +        j.tools.alerthandler.alert_raise(
    +            app_name="BackupJob",
    +            category="exception",
    +            message=f"BackupJob name: {self.instance_name} - Error happened during Backing up using this ResticRepo: {client_name}",
    +            alert_type="exception",
    +            traceback=g.exception.__traceback__,
    +        )
    +
    +    def _on_success(self, g: Greenlet):
    +        client_name = self.clients[self._greenlets.index(g)]
    +        j.logger.info(f"BackupJob name: {self.instance_name} - ResticRepo: {client_name} snapshot successfully saved.")
    +
    +    def execute(self, block=False):
    +        """Backups the preconfigured paths with the preconfigured restic clients.
    +        All snapshots created with a Backupjob will be tagged with the BackupJob instance name for easy referencing, manageing, cleaning and restoring.
    +
    +        Args:
    +            block (bool, optional): Wait for the backup to finish. if False, will start the backup and return immediately. Defaults to False.
    +
    +        Raises:
    +            j.exceptions.Runtime: If there are no restic instances defined for this backup job.
    +
    +        Returns:
    +            bool: whether the backup created successfully on all the preconfigured repos.
    +            if block is False, then it returns False immediately.
    +        """
    +
    +        def _excute(client_name, paths, tags, exclude):
    +            client = self._get_client(client_name)
    +            client.backup(paths, tags=tags, exclude=exclude)
    +
    +        paths = [fs.os.path.expanduser(path) for path in self.paths]
    +        paths_to_exclude = [fs.os.path.expanduser(path) for path in self.paths_to_exclude]
    +        self._greenlets = []
    +        if not self.clients:
    +            raise j.exceptions.Runtime("Can't execute backup job no restic instances defined.")
    +        for restic_client_name in self.clients:
    +            self._greenlets.append(
    +                gevent.spawn(_excute, restic_client_name, paths, tags=[self.instance_name], exclude=paths_to_exclude)
    +            )
    +            self._greenlets[-1].link_exception(self._on_exception)
    +            self._greenlets[-1].link_value(self._on_success)
    +
    +        if block:
    +            gevent.joinall(self._greenlets)
    +        return all([greenlet.successful() for greenlet in self._greenlets])
    +
    +    def list_all_snapshots(self, last=False, path=None):
    +        """Returns a dictionary of restic snapshots lists that are related to to this BackupJob instance,
    +        where the keys are the ResticRepo instance name.
    +
    +        Args:
    +            last (bool, optional): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +            path (str, optional): Path to filter on. Defaults to None.
    +
    +        Returns:
    +            Dict of lists: a dictionary of restic snapshots lists
    +        """
    +        snapshots = {}
    +        for restic_client_name in self.clients:
    +            snapshots[restic_client_name] = self.list_snapshots(restic_client_name, last=last, path=path)
    +        return snapshots
    +
    +    def list_snapshots(self, restic_client_name, last=False, path=None):
    +        """Returns a list of restic snapshots that are related to to this BackupJob instance from a ResticRepo with a given instance name
    +
    +        Args:
    +            restic_client_name (str): Restic instance name.
    +            last (bool, optional): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +            path (str, optional): Path to filter on. Defaults to None.
    +
    +        Returns:
    +            list of dictionaries: list of restic snapshots.
    +                Example: [{'time': '2021-06-27T19:18:00.203093762+02:00', 'parent': 'ded571a29dfa8f3db1c455ee5714acf5b248a90f9b5103235a682737eba583b3',
    +                'tree': 'dae692c71558aa6f1f632dc805dd614a8d35ecb8c5053bc32665506d7a4a066c', 'paths': ['/home/ayoub/play.txt'],
    +                'hostname': 'ayoub', 'username': 'ayoub', 'uid': 1000, 'gid': 1000, 'tags': ['admin_sameh'],
    +                'id': 'e3d5d9dd2e252d0cf55ff66aabe839af312c4fdc6119e08a72984086664ef3b0', 'short_id': 'e3d5d9dd'}]
    +        """
    +        client = self._get_client(restic_client_name)
    +        return client.list_snapshots(tags=[self.instance_name], last=last, path=path) or []
    +
    +    def restore(self, restic_client_name, target_path="/", snapshot_id=None, host=None):
    +        """Restore a specifc or latest snapshot for this BackupJob from a ResticRepo with a given instance name.
    +
    +        Args:
    +            restic_client_name (str): Restic instance name.
    +            target_path (str, optional): path to restore to. Defaults to "/".
    +            snapshot_id (str, optional):  id or short_id of the snapshot.
    +                if not specified will use tha latest snapshot/s taken for this BackupJob instead. Defaults to None.
    +            host (str, optional): Filter on the hostname when using latest. Defaults to None.
    +
    +        Raises:
    +            j.exceptions.Value: if the specified snapshot id is not found for this BackupJob.
    +            j.exceptions.Runtime: if no previous snapshots found for this BackupJob.
    +        """
    +        client = self._get_client(restic_client_name)
    +        if snapshot_id:
    +            if len(snapshot_id) < 8:
    +                raise j.exceptions.Value(f"The length of snapshot id ({snapshot_id}) should be at least 8 characters.")
    +            snapshots = self.list_snapshots(restic_client_name)
    +            if not snapshots:
    +                raise j.exceptions.Runtime(f"no previous snapshots found for this backup job {self.instance_name}.")
    +            snapshots_ids = [snapshot["id"] for snapshot in snapshots if snapshot["id"].startswith(snapshot_id)]
    +            if not snapshots_ids:
    +                raise j.exceptions.Value(
    +                    f"This snapshot id {snapshot_id:.8} is not found for this backup job {self.instance_name}."
    +                )
    +
    +        client.restore(target_path, snapshot_id=snapshot_id, tags=[self.instance_name], host=host)
    +
    +    def clean_snapshots(self, restic_client_name, keep_last=0, prune=True):
    +        """Deletes the snapshots data if `prune` is True otherwise remove the)reference to the data (snapshots) in a ResticRepo with a given instance name.
    +
    +        Args:
    +            restic_client_name (str): Restic instance name.
    +            keep_last (int, optional): How many snapshots to keep. Passing 0 will remove all snapshots for this BackupJob instance. Defaults to 0.
    +            prune (bool, optional):  Whether to delete the data or not. Defaults to True.
    +        """
    +        client = self._get_client(restic_client_name)
    +        if not keep_last:
    +            # first forget all snapshots but last as restic will not allow values less than 1.
    +            client.forget(keep_last=1, tags=[self.instance_name], prune=prune)
    +            # then get snapshots ids of last snapshots and forget them
    +            last_snapshots = self.list_snapshots(restic_client_name, last=True)
    +            last_snapshots_ids = [snapshot["id"] for snapshot in last_snapshots]
    +            return client.forget(keep_last=0, tags=[self.instance_name], prune=prune, snapshots=last_snapshots_ids)
    +        client.forget(keep_last=keep_last, tags=[self.instance_name], prune=prune)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class BackupJob +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class BackupJob(Base):
    +    paths = fields.List(fields.String(validators=[_path_validator]))
    +    paths_to_exclude = fields.List(fields.String())
    +    clients = fields.List(fields.String(validators=[_client_validator]))
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +    @staticmethod
    +    def _get_client(restic_client_name):
    +        """Gets a ResticRepo object with a given instance name.
    +
    +        Args:
    +            restic_client_name (str): Restic instance name.
    +
    +        Raises:
    +            j.exceptions.Runtime: If restic instance not found.
    +
    +        Returns:
    +            ResticRepo: jumpscale.tools.restic.restic.ResticRepo instance.
    +        """
    +        if restic_client_name in j.tools.restic.list_all():
    +            return j.tools.restic.get(restic_client_name)
    +        raise j.exceptions.Runtime(f"The restic client: {restic_client_name} not found!")
    +
    +    def _on_exception(self, g: Greenlet):
    +        client_name = self.clients[self._greenlets.index(g)]
    +        j.logger.exception(
    +            f"BackupJob name: {self.instance_name} - Error happened during Backing up using this ResticRepo: {client_name}",
    +            exception=g.exception,
    +        )
    +        j.tools.alerthandler.alert_raise(
    +            app_name="BackupJob",
    +            category="exception",
    +            message=f"BackupJob name: {self.instance_name} - Error happened during Backing up using this ResticRepo: {client_name}",
    +            alert_type="exception",
    +            traceback=g.exception.__traceback__,
    +        )
    +
    +    def _on_success(self, g: Greenlet):
    +        client_name = self.clients[self._greenlets.index(g)]
    +        j.logger.info(f"BackupJob name: {self.instance_name} - ResticRepo: {client_name} snapshot successfully saved.")
    +
    +    def execute(self, block=False):
    +        """Backups the preconfigured paths with the preconfigured restic clients.
    +        All snapshots created with a Backupjob will be tagged with the BackupJob instance name for easy referencing, manageing, cleaning and restoring.
    +
    +        Args:
    +            block (bool, optional): Wait for the backup to finish. if False, will start the backup and return immediately. Defaults to False.
    +
    +        Raises:
    +            j.exceptions.Runtime: If there are no restic instances defined for this backup job.
    +
    +        Returns:
    +            bool: whether the backup created successfully on all the preconfigured repos.
    +            if block is False, then it returns False immediately.
    +        """
    +
    +        def _excute(client_name, paths, tags, exclude):
    +            client = self._get_client(client_name)
    +            client.backup(paths, tags=tags, exclude=exclude)
    +
    +        paths = [fs.os.path.expanduser(path) for path in self.paths]
    +        paths_to_exclude = [fs.os.path.expanduser(path) for path in self.paths_to_exclude]
    +        self._greenlets = []
    +        if not self.clients:
    +            raise j.exceptions.Runtime("Can't execute backup job no restic instances defined.")
    +        for restic_client_name in self.clients:
    +            self._greenlets.append(
    +                gevent.spawn(_excute, restic_client_name, paths, tags=[self.instance_name], exclude=paths_to_exclude)
    +            )
    +            self._greenlets[-1].link_exception(self._on_exception)
    +            self._greenlets[-1].link_value(self._on_success)
    +
    +        if block:
    +            gevent.joinall(self._greenlets)
    +        return all([greenlet.successful() for greenlet in self._greenlets])
    +
    +    def list_all_snapshots(self, last=False, path=None):
    +        """Returns a dictionary of restic snapshots lists that are related to to this BackupJob instance,
    +        where the keys are the ResticRepo instance name.
    +
    +        Args:
    +            last (bool, optional): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +            path (str, optional): Path to filter on. Defaults to None.
    +
    +        Returns:
    +            Dict of lists: a dictionary of restic snapshots lists
    +        """
    +        snapshots = {}
    +        for restic_client_name in self.clients:
    +            snapshots[restic_client_name] = self.list_snapshots(restic_client_name, last=last, path=path)
    +        return snapshots
    +
    +    def list_snapshots(self, restic_client_name, last=False, path=None):
    +        """Returns a list of restic snapshots that are related to to this BackupJob instance from a ResticRepo with a given instance name
    +
    +        Args:
    +            restic_client_name (str): Restic instance name.
    +            last (bool, optional): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +            path (str, optional): Path to filter on. Defaults to None.
    +
    +        Returns:
    +            list of dictionaries: list of restic snapshots.
    +                Example: [{'time': '2021-06-27T19:18:00.203093762+02:00', 'parent': 'ded571a29dfa8f3db1c455ee5714acf5b248a90f9b5103235a682737eba583b3',
    +                'tree': 'dae692c71558aa6f1f632dc805dd614a8d35ecb8c5053bc32665506d7a4a066c', 'paths': ['/home/ayoub/play.txt'],
    +                'hostname': 'ayoub', 'username': 'ayoub', 'uid': 1000, 'gid': 1000, 'tags': ['admin_sameh'],
    +                'id': 'e3d5d9dd2e252d0cf55ff66aabe839af312c4fdc6119e08a72984086664ef3b0', 'short_id': 'e3d5d9dd'}]
    +        """
    +        client = self._get_client(restic_client_name)
    +        return client.list_snapshots(tags=[self.instance_name], last=last, path=path) or []
    +
    +    def restore(self, restic_client_name, target_path="/", snapshot_id=None, host=None):
    +        """Restore a specifc or latest snapshot for this BackupJob from a ResticRepo with a given instance name.
    +
    +        Args:
    +            restic_client_name (str): Restic instance name.
    +            target_path (str, optional): path to restore to. Defaults to "/".
    +            snapshot_id (str, optional):  id or short_id of the snapshot.
    +                if not specified will use tha latest snapshot/s taken for this BackupJob instead. Defaults to None.
    +            host (str, optional): Filter on the hostname when using latest. Defaults to None.
    +
    +        Raises:
    +            j.exceptions.Value: if the specified snapshot id is not found for this BackupJob.
    +            j.exceptions.Runtime: if no previous snapshots found for this BackupJob.
    +        """
    +        client = self._get_client(restic_client_name)
    +        if snapshot_id:
    +            if len(snapshot_id) < 8:
    +                raise j.exceptions.Value(f"The length of snapshot id ({snapshot_id}) should be at least 8 characters.")
    +            snapshots = self.list_snapshots(restic_client_name)
    +            if not snapshots:
    +                raise j.exceptions.Runtime(f"no previous snapshots found for this backup job {self.instance_name}.")
    +            snapshots_ids = [snapshot["id"] for snapshot in snapshots if snapshot["id"].startswith(snapshot_id)]
    +            if not snapshots_ids:
    +                raise j.exceptions.Value(
    +                    f"This snapshot id {snapshot_id:.8} is not found for this backup job {self.instance_name}."
    +                )
    +
    +        client.restore(target_path, snapshot_id=snapshot_id, tags=[self.instance_name], host=host)
    +
    +    def clean_snapshots(self, restic_client_name, keep_last=0, prune=True):
    +        """Deletes the snapshots data if `prune` is True otherwise remove the)reference to the data (snapshots) in a ResticRepo with a given instance name.
    +
    +        Args:
    +            restic_client_name (str): Restic instance name.
    +            keep_last (int, optional): How many snapshots to keep. Passing 0 will remove all snapshots for this BackupJob instance. Defaults to 0.
    +            prune (bool, optional):  Whether to delete the data or not. Defaults to True.
    +        """
    +        client = self._get_client(restic_client_name)
    +        if not keep_last:
    +            # first forget all snapshots but last as restic will not allow values less than 1.
    +            client.forget(keep_last=1, tags=[self.instance_name], prune=prune)
    +            # then get snapshots ids of last snapshots and forget them
    +            last_snapshots = self.list_snapshots(restic_client_name, last=True)
    +            last_snapshots_ids = [snapshot["id"] for snapshot in last_snapshots]
    +            return client.forget(keep_last=0, tags=[self.instance_name], prune=prune, snapshots=last_snapshots_ids)
    +        client.forget(keep_last=keep_last, tags=[self.instance_name], prune=prune)
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var clients
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var paths
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var paths_to_exclude
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def clean_snapshots(self, restic_client_name, keep_last=0, prune=True) +
    +
    +

    Deletes the snapshots data if prune is True otherwise remove the)reference to the data (snapshots) in a ResticRepo with a given instance name.

    +

    Args

    +
    +
    restic_client_name : str
    +
    Restic instance name.
    +
    keep_last : int, optional
    +
    How many snapshots to keep. Passing 0 will remove all snapshots for this BackupJob instance. Defaults to 0.
    +
    prune : bool, optional
    +
    Whether to delete the data or not. Defaults to True.
    +
    +
    + +Expand source code + +
    def clean_snapshots(self, restic_client_name, keep_last=0, prune=True):
    +    """Deletes the snapshots data if `prune` is True otherwise remove the)reference to the data (snapshots) in a ResticRepo with a given instance name.
    +
    +    Args:
    +        restic_client_name (str): Restic instance name.
    +        keep_last (int, optional): How many snapshots to keep. Passing 0 will remove all snapshots for this BackupJob instance. Defaults to 0.
    +        prune (bool, optional):  Whether to delete the data or not. Defaults to True.
    +    """
    +    client = self._get_client(restic_client_name)
    +    if not keep_last:
    +        # first forget all snapshots but last as restic will not allow values less than 1.
    +        client.forget(keep_last=1, tags=[self.instance_name], prune=prune)
    +        # then get snapshots ids of last snapshots and forget them
    +        last_snapshots = self.list_snapshots(restic_client_name, last=True)
    +        last_snapshots_ids = [snapshot["id"] for snapshot in last_snapshots]
    +        return client.forget(keep_last=0, tags=[self.instance_name], prune=prune, snapshots=last_snapshots_ids)
    +    client.forget(keep_last=keep_last, tags=[self.instance_name], prune=prune)
    +
    +
    +
    +def execute(self, block=False) +
    +
    +

    Backups the preconfigured paths with the preconfigured restic clients. +All snapshots created with a Backupjob will be tagged with the BackupJob instance name for easy referencing, manageing, cleaning and restoring.

    +

    Args

    +
    +
    block : bool, optional
    +
    Wait for the backup to finish. if False, will start the backup and return immediately. Defaults to False.
    +
    +

    Raises

    +
    +
    j.exceptions.Runtime
    +
    If there are no restic instances defined for this backup job.
    +
    +

    Returns

    +
    +
    bool
    +
    whether the backup created successfully on all the preconfigured repos.
    +
    +

    if block is False, then it returns False immediately.

    +
    + +Expand source code + +
    def execute(self, block=False):
    +    """Backups the preconfigured paths with the preconfigured restic clients.
    +    All snapshots created with a Backupjob will be tagged with the BackupJob instance name for easy referencing, manageing, cleaning and restoring.
    +
    +    Args:
    +        block (bool, optional): Wait for the backup to finish. if False, will start the backup and return immediately. Defaults to False.
    +
    +    Raises:
    +        j.exceptions.Runtime: If there are no restic instances defined for this backup job.
    +
    +    Returns:
    +        bool: whether the backup created successfully on all the preconfigured repos.
    +        if block is False, then it returns False immediately.
    +    """
    +
    +    def _excute(client_name, paths, tags, exclude):
    +        client = self._get_client(client_name)
    +        client.backup(paths, tags=tags, exclude=exclude)
    +
    +    paths = [fs.os.path.expanduser(path) for path in self.paths]
    +    paths_to_exclude = [fs.os.path.expanduser(path) for path in self.paths_to_exclude]
    +    self._greenlets = []
    +    if not self.clients:
    +        raise j.exceptions.Runtime("Can't execute backup job no restic instances defined.")
    +    for restic_client_name in self.clients:
    +        self._greenlets.append(
    +            gevent.spawn(_excute, restic_client_name, paths, tags=[self.instance_name], exclude=paths_to_exclude)
    +        )
    +        self._greenlets[-1].link_exception(self._on_exception)
    +        self._greenlets[-1].link_value(self._on_success)
    +
    +    if block:
    +        gevent.joinall(self._greenlets)
    +    return all([greenlet.successful() for greenlet in self._greenlets])
    +
    +
    +
    +def list_all_snapshots(self, last=False, path=None) +
    +
    +

    Returns a dictionary of restic snapshots lists that are related to to this BackupJob instance, +where the keys are the ResticRepo instance name.

    +

    Args

    +
    +
    last : bool, optional
    +
    If True will get last snapshot only while respecting the other filters. Defaults to False.
    +
    path : str, optional
    +
    Path to filter on. Defaults to None.
    +
    +

    Returns

    +
    +
    Dict of lists
    +
    a dictionary of restic snapshots lists
    +
    +
    + +Expand source code + +
    def list_all_snapshots(self, last=False, path=None):
    +    """Returns a dictionary of restic snapshots lists that are related to to this BackupJob instance,
    +    where the keys are the ResticRepo instance name.
    +
    +    Args:
    +        last (bool, optional): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +        path (str, optional): Path to filter on. Defaults to None.
    +
    +    Returns:
    +        Dict of lists: a dictionary of restic snapshots lists
    +    """
    +    snapshots = {}
    +    for restic_client_name in self.clients:
    +        snapshots[restic_client_name] = self.list_snapshots(restic_client_name, last=last, path=path)
    +    return snapshots
    +
    +
    +
    +def list_snapshots(self, restic_client_name, last=False, path=None) +
    +
    +

    Returns a list of restic snapshots that are related to to this BackupJob instance from a ResticRepo with a given instance name

    +

    Args

    +
    +
    restic_client_name : str
    +
    Restic instance name.
    +
    last : bool, optional
    +
    If True will get last snapshot only while respecting the other filters. Defaults to False.
    +
    path : str, optional
    +
    Path to filter on. Defaults to None.
    +
    +

    Returns

    +
    +
    list of dictionaries
    +
    list of restic snapshots. +Example: [{'time': '2021-06-27T19:18:00.203093762+02:00', 'parent': 'ded571a29dfa8f3db1c455ee5714acf5b248a90f9b5103235a682737eba583b3', +'tree': 'dae692c71558aa6f1f632dc805dd614a8d35ecb8c5053bc32665506d7a4a066c', 'paths': ['/home/ayoub/play.txt'], +'hostname': 'ayoub', 'username': 'ayoub', 'uid': 1000, 'gid': 1000, 'tags': ['admin_sameh'], +'id': 'e3d5d9dd2e252d0cf55ff66aabe839af312c4fdc6119e08a72984086664ef3b0', 'short_id': 'e3d5d9dd'}]
    +
    +
    + +Expand source code + +
    def list_snapshots(self, restic_client_name, last=False, path=None):
    +    """Returns a list of restic snapshots that are related to to this BackupJob instance from a ResticRepo with a given instance name
    +
    +    Args:
    +        restic_client_name (str): Restic instance name.
    +        last (bool, optional): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +        path (str, optional): Path to filter on. Defaults to None.
    +
    +    Returns:
    +        list of dictionaries: list of restic snapshots.
    +            Example: [{'time': '2021-06-27T19:18:00.203093762+02:00', 'parent': 'ded571a29dfa8f3db1c455ee5714acf5b248a90f9b5103235a682737eba583b3',
    +            'tree': 'dae692c71558aa6f1f632dc805dd614a8d35ecb8c5053bc32665506d7a4a066c', 'paths': ['/home/ayoub/play.txt'],
    +            'hostname': 'ayoub', 'username': 'ayoub', 'uid': 1000, 'gid': 1000, 'tags': ['admin_sameh'],
    +            'id': 'e3d5d9dd2e252d0cf55ff66aabe839af312c4fdc6119e08a72984086664ef3b0', 'short_id': 'e3d5d9dd'}]
    +    """
    +    client = self._get_client(restic_client_name)
    +    return client.list_snapshots(tags=[self.instance_name], last=last, path=path) or []
    +
    +
    +
    +def restore(self, restic_client_name, target_path='/', snapshot_id=None, host=None) +
    +
    +

    Restore a specifc or latest snapshot for this BackupJob from a ResticRepo with a given instance name.

    +

    Args

    +
    +
    restic_client_name : str
    +
    Restic instance name.
    +
    target_path : str, optional
    +
    path to restore to. Defaults to "/".
    +
    snapshot_id : str, optional
    +
    id or short_id of the snapshot. +if not specified will use tha latest snapshot/s taken for this BackupJob instead. Defaults to None.
    +
    host : str, optional
    +
    Filter on the hostname when using latest. Defaults to None.
    +
    +

    Raises

    +
    +
    j.exceptions.Value
    +
    if the specified snapshot id is not found for this BackupJob.
    +
    j.exceptions.Runtime
    +
    if no previous snapshots found for this BackupJob.
    +
    +
    + +Expand source code + +
    def restore(self, restic_client_name, target_path="/", snapshot_id=None, host=None):
    +    """Restore a specifc or latest snapshot for this BackupJob from a ResticRepo with a given instance name.
    +
    +    Args:
    +        restic_client_name (str): Restic instance name.
    +        target_path (str, optional): path to restore to. Defaults to "/".
    +        snapshot_id (str, optional):  id or short_id of the snapshot.
    +            if not specified will use tha latest snapshot/s taken for this BackupJob instead. Defaults to None.
    +        host (str, optional): Filter on the hostname when using latest. Defaults to None.
    +
    +    Raises:
    +        j.exceptions.Value: if the specified snapshot id is not found for this BackupJob.
    +        j.exceptions.Runtime: if no previous snapshots found for this BackupJob.
    +    """
    +    client = self._get_client(restic_client_name)
    +    if snapshot_id:
    +        if len(snapshot_id) < 8:
    +            raise j.exceptions.Value(f"The length of snapshot id ({snapshot_id}) should be at least 8 characters.")
    +        snapshots = self.list_snapshots(restic_client_name)
    +        if not snapshots:
    +            raise j.exceptions.Runtime(f"no previous snapshots found for this backup job {self.instance_name}.")
    +        snapshots_ids = [snapshot["id"] for snapshot in snapshots if snapshot["id"].startswith(snapshot_id)]
    +        if not snapshots_ids:
    +            raise j.exceptions.Value(
    +                f"This snapshot id {snapshot_id:.8} is not found for this backup job {self.instance_name}."
    +            )
    +
    +    client.restore(target_path, snapshot_id=snapshot_id, tags=[self.instance_name], host=host)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/backupjob/index.html b/docs/api/jumpscale/sals/backupjob/index.html new file mode 100644 index 000000000..3b3752d3f --- /dev/null +++ b/docs/api/jumpscale/sals/backupjob/index.html @@ -0,0 +1,100 @@ + + + + + + +jumpscale.sals.backupjob API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.backupjob

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .backupjob import BackupJob
    +
    +    return StoredFactory(BackupJob)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.sals.backupjob.backupjob
    +
    +

    THE BACKUPJOB SAL

    +

    This sal can be used to create and manage multiple backup jobs with multiple and configured paths …

    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .backupjob import BackupJob
    +
    +    return StoredFactory(BackupJob)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/billing/billing.html b/docs/api/jumpscale/sals/billing/billing.html new file mode 100644 index 000000000..08c606601 --- /dev/null +++ b/docs/api/jumpscale/sals/billing/billing.html @@ -0,0 +1,634 @@ + + + + + + +jumpscale.sals.billing.billing API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.billing.billing

    +
    +
    +
    + +Expand source code + +
    from .models import PAYMENT_FACTORY, REFUND_FACTORY
    +import uuid, datetime
    +import gevent
    +from jumpscale.loader import j
    +from jumpscale.clients.stellar import TRANSACTION_FEES
    +
    +
    +class BillingManager:
    +    def submit_payment(self, amount, wallet_name, refund_extra=True, expiry=5, description=""):
    +        """Submit payment.
    +
    +        Args:
    +            amount (float): amount of current payment.
    +            wallet_name (str): name of wallet that submit the payment.
    +            refund_extra (bool, optional): if the payment has extra amount it will be refunded. Defaults to True.
    +            expiry (int, optional): time in minutes the payment will expire after it. Defaults to 5.
    +            description (str, optional): describe the payment. Defaults to "".
    +
    +        Returns:
    +            tuple(str, str): payment_id and memo_text that used as confirmation for the payment.
    +        """
    +        payment_id = uuid.uuid4().hex
    +        instance_name = f"payment_{payment_id}"
    +        payment = PAYMENT_FACTORY.new(
    +            instance_name,
    +            payment_id=payment_id,
    +            amount=round(amount, 6),
    +            wallet_name=wallet_name,
    +            refund_extra=refund_extra,
    +            description=description,
    +        )
    +        payment.deadline = datetime.datetime.utcnow() + datetime.timedelta(minutes=expiry)
    +        payment.save()
    +        j.logger.info(
    +            f"payment {payment_id} submitted for wallet: {wallet_name} amount: {amount}, description: {description}, expiry: {expiry}, memo_text: {payment.memo_text}"
    +        )
    +        return payment_id, payment.memo_text
    +
    +    def wait_payment(self, payment_id, bot=None, notes=None):
    +        """Wait payment amount of time.
    +
    +        Args:
    +            payment_id (str): payment id to wait on.
    +            bot : used in case of using it with threebot deployer or VDC chatflow. Defaults to None.
    +            notes (str, optional): optional note that appears while waiting. Defaults to None.
    +
    +        Returns:
    +            bool: return result of this payment, True if payment finished, False if payment expired.
    +        """
    +        j.logger.info(f"waiting payment: {payment_id}")
    +        payment = PAYMENT_FACTORY.find_by_id(payment_id)
    +        if bot:
    +            self._show_payment(bot, payment, notes)
    +
    +        while not payment.is_finished():
    +            gevent.sleep(3)
    +            payment = PAYMENT_FACTORY.find_by_id(payment_id)
    +        j.logger.info(f"payment: {payment_id} result {payment.result.success}")
    +        return payment.result.success
    +
    +    def _show_payment(self, bot, payment_obj, notes=None):
    +        notes = notes or []
    +        qr_code = (
    +            f"TFT:{payment_obj.wallet.address}?amount={payment_obj.amount}&message={payment_obj.memo_text}&sender=me"
    +        )
    +        notes_text = "\n".join([f"<h4>Note: {note}</h4>" for note in notes])
    +        qr_encoded = j.tools.qrcode.base64_get(qr_code, scale=2)
    +        msg_text = f"""Please scan the QR Code below (Using ThreeFold Connect Application) for the payment details
    +        <div class="text-center">
    +            <img style="border:1px dashed #85929E" src="data:image/png;base64,{qr_encoded}"/>
    +        </div>
    +        <h4> Destination Wallet Address: </h4>  {payment_obj.wallet.address} \n
    +        <h4> Currency: </h4>  TFT \n
    +        <h4> Memo Text (Message): </h4>  {payment_obj.memo_text} \n
    +        <h4> Total Amount: </h4>  {payment_obj.amount} TFT \n
    +        {notes_text}
    +        <h5>When using manual payment please note that inserting the memo-text is an important way to identify a transaction recipient beyond a wallet address. Failure to do so will result in a failed payment. Please also keep in mind that an additional Transaction fee of {TRANSACTION_FEES} TFT will automatically occur per transaction.</h5>
    +        """
    +        bot.md_show_update(msg_text, html=True)
    +
    +    def refund_failed_payments(self):
    +        """Refund any failed payment.
    +        Example: transfer amount less than payment amount.
    +        """
    +        for payment in PAYMENT_FACTORY.list_failed_payments():
    +            refund_result = True
    +            for transaction in payment.result.transactions:
    +                if transaction.success or transaction.transaction_refund.success:
    +                    continue
    +                j.logger.info(f"refunding transaction: {transaction.transaction_hash} of payment: {payment.payment_id}")
    +                refund_result = refund_result and transaction.refund(payment.wallet)
    +                payment.save()
    +
    +    def issue_refund(self, payment_id, amount=-1):
    +        """Issue Refund.
    +        It is used if refund needed even after successfull payment.
    +        Args:
    +            payment_id (str): payment id of the payment needs to be refunded.
    +            amount (int, optional): amount of the refund value. Defaults to -1(Meaning refund the total amount of the payment).
    +        """
    +        instance_name = f"refund_{payment_id}"
    +        request = REFUND_FACTORY.new(instance_name, payment_id=payment_id, amount=amount)
    +        request.save()
    +        j.logger.info(f"refund request created for payment: {payment_id}")
    +        return
    +
    +    def check_refund(self, payment_id):
    +        """Check refund request status.
    +
    +        Args:
    +            payment_id (str): payment id of the payment needs to check refund status.
    +
    +        Raises:
    +            j.exceptions.Input: raise error if no refund request belong to the payment.
    +
    +        Returns:
    +            bool: return the state of the refund request, True if refund success.
    +        """
    +        instance_name = f"refund_{payment_id}"
    +        request = REFUND_FACTORY.find(instance_name)
    +        if not request:
    +            raise j.exceptions.Input(f"not refunds were issues for payment {payment_id}")
    +        return request.success
    +
    +    def process_refunds(self):
    +        """Process any active refund.
    +        list all active refund and apply it.
    +        """
    +        for request in REFUND_FACTORY.list_active_requests():
    +            j.logger.info(f"applying active refund for payment: {request.payment_id}")
    +            request.apply()
    +
    +    def process_payments(self):
    +        """Process any active payment.
    +        list all active payment and apply it.
    +        """
    +        for payment in PAYMENT_FACTORY.list_active_payments():
    +            j.logger.info(f"updating active payment: {payment.payment_id}")
    +            payment.update_status()
    +
    +    def refund_extra(self):
    +        """Process any active extra refund.
    +        list all active extra refund and apply it.
    +        """
    +        for payment in PAYMENT_FACTORY.list_extra_paid_payments():
    +            j.logger.info(f"refund extra paid for payment: {payment.payment_id}")
    +            payment.result.refund_extra()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class BillingManager +
    +
    +
    +
    + +Expand source code + +
    class BillingManager:
    +    def submit_payment(self, amount, wallet_name, refund_extra=True, expiry=5, description=""):
    +        """Submit payment.
    +
    +        Args:
    +            amount (float): amount of current payment.
    +            wallet_name (str): name of wallet that submit the payment.
    +            refund_extra (bool, optional): if the payment has extra amount it will be refunded. Defaults to True.
    +            expiry (int, optional): time in minutes the payment will expire after it. Defaults to 5.
    +            description (str, optional): describe the payment. Defaults to "".
    +
    +        Returns:
    +            tuple(str, str): payment_id and memo_text that used as confirmation for the payment.
    +        """
    +        payment_id = uuid.uuid4().hex
    +        instance_name = f"payment_{payment_id}"
    +        payment = PAYMENT_FACTORY.new(
    +            instance_name,
    +            payment_id=payment_id,
    +            amount=round(amount, 6),
    +            wallet_name=wallet_name,
    +            refund_extra=refund_extra,
    +            description=description,
    +        )
    +        payment.deadline = datetime.datetime.utcnow() + datetime.timedelta(minutes=expiry)
    +        payment.save()
    +        j.logger.info(
    +            f"payment {payment_id} submitted for wallet: {wallet_name} amount: {amount}, description: {description}, expiry: {expiry}, memo_text: {payment.memo_text}"
    +        )
    +        return payment_id, payment.memo_text
    +
    +    def wait_payment(self, payment_id, bot=None, notes=None):
    +        """Wait payment amount of time.
    +
    +        Args:
    +            payment_id (str): payment id to wait on.
    +            bot : used in case of using it with threebot deployer or VDC chatflow. Defaults to None.
    +            notes (str, optional): optional note that appears while waiting. Defaults to None.
    +
    +        Returns:
    +            bool: return result of this payment, True if payment finished, False if payment expired.
    +        """
    +        j.logger.info(f"waiting payment: {payment_id}")
    +        payment = PAYMENT_FACTORY.find_by_id(payment_id)
    +        if bot:
    +            self._show_payment(bot, payment, notes)
    +
    +        while not payment.is_finished():
    +            gevent.sleep(3)
    +            payment = PAYMENT_FACTORY.find_by_id(payment_id)
    +        j.logger.info(f"payment: {payment_id} result {payment.result.success}")
    +        return payment.result.success
    +
    +    def _show_payment(self, bot, payment_obj, notes=None):
    +        notes = notes or []
    +        qr_code = (
    +            f"TFT:{payment_obj.wallet.address}?amount={payment_obj.amount}&message={payment_obj.memo_text}&sender=me"
    +        )
    +        notes_text = "\n".join([f"<h4>Note: {note}</h4>" for note in notes])
    +        qr_encoded = j.tools.qrcode.base64_get(qr_code, scale=2)
    +        msg_text = f"""Please scan the QR Code below (Using ThreeFold Connect Application) for the payment details
    +        <div class="text-center">
    +            <img style="border:1px dashed #85929E" src="data:image/png;base64,{qr_encoded}"/>
    +        </div>
    +        <h4> Destination Wallet Address: </h4>  {payment_obj.wallet.address} \n
    +        <h4> Currency: </h4>  TFT \n
    +        <h4> Memo Text (Message): </h4>  {payment_obj.memo_text} \n
    +        <h4> Total Amount: </h4>  {payment_obj.amount} TFT \n
    +        {notes_text}
    +        <h5>When using manual payment please note that inserting the memo-text is an important way to identify a transaction recipient beyond a wallet address. Failure to do so will result in a failed payment. Please also keep in mind that an additional Transaction fee of {TRANSACTION_FEES} TFT will automatically occur per transaction.</h5>
    +        """
    +        bot.md_show_update(msg_text, html=True)
    +
    +    def refund_failed_payments(self):
    +        """Refund any failed payment.
    +        Example: transfer amount less than payment amount.
    +        """
    +        for payment in PAYMENT_FACTORY.list_failed_payments():
    +            refund_result = True
    +            for transaction in payment.result.transactions:
    +                if transaction.success or transaction.transaction_refund.success:
    +                    continue
    +                j.logger.info(f"refunding transaction: {transaction.transaction_hash} of payment: {payment.payment_id}")
    +                refund_result = refund_result and transaction.refund(payment.wallet)
    +                payment.save()
    +
    +    def issue_refund(self, payment_id, amount=-1):
    +        """Issue Refund.
    +        It is used if refund needed even after successfull payment.
    +        Args:
    +            payment_id (str): payment id of the payment needs to be refunded.
    +            amount (int, optional): amount of the refund value. Defaults to -1(Meaning refund the total amount of the payment).
    +        """
    +        instance_name = f"refund_{payment_id}"
    +        request = REFUND_FACTORY.new(instance_name, payment_id=payment_id, amount=amount)
    +        request.save()
    +        j.logger.info(f"refund request created for payment: {payment_id}")
    +        return
    +
    +    def check_refund(self, payment_id):
    +        """Check refund request status.
    +
    +        Args:
    +            payment_id (str): payment id of the payment needs to check refund status.
    +
    +        Raises:
    +            j.exceptions.Input: raise error if no refund request belong to the payment.
    +
    +        Returns:
    +            bool: return the state of the refund request, True if refund success.
    +        """
    +        instance_name = f"refund_{payment_id}"
    +        request = REFUND_FACTORY.find(instance_name)
    +        if not request:
    +            raise j.exceptions.Input(f"not refunds were issues for payment {payment_id}")
    +        return request.success
    +
    +    def process_refunds(self):
    +        """Process any active refund.
    +        list all active refund and apply it.
    +        """
    +        for request in REFUND_FACTORY.list_active_requests():
    +            j.logger.info(f"applying active refund for payment: {request.payment_id}")
    +            request.apply()
    +
    +    def process_payments(self):
    +        """Process any active payment.
    +        list all active payment and apply it.
    +        """
    +        for payment in PAYMENT_FACTORY.list_active_payments():
    +            j.logger.info(f"updating active payment: {payment.payment_id}")
    +            payment.update_status()
    +
    +    def refund_extra(self):
    +        """Process any active extra refund.
    +        list all active extra refund and apply it.
    +        """
    +        for payment in PAYMENT_FACTORY.list_extra_paid_payments():
    +            j.logger.info(f"refund extra paid for payment: {payment.payment_id}")
    +            payment.result.refund_extra()
    +
    +

    Methods

    +
    +
    +def check_refund(self, payment_id) +
    +
    +

    Check refund request status.

    +

    Args

    +
    +
    payment_id : str
    +
    payment id of the payment needs to check refund status.
    +
    +

    Raises

    +
    +
    j.exceptions.Input
    +
    raise error if no refund request belong to the payment.
    +
    +

    Returns

    +
    +
    bool
    +
    return the state of the refund request, True if refund success.
    +
    +
    + +Expand source code + +
    def check_refund(self, payment_id):
    +    """Check refund request status.
    +
    +    Args:
    +        payment_id (str): payment id of the payment needs to check refund status.
    +
    +    Raises:
    +        j.exceptions.Input: raise error if no refund request belong to the payment.
    +
    +    Returns:
    +        bool: return the state of the refund request, True if refund success.
    +    """
    +    instance_name = f"refund_{payment_id}"
    +    request = REFUND_FACTORY.find(instance_name)
    +    if not request:
    +        raise j.exceptions.Input(f"not refunds were issues for payment {payment_id}")
    +    return request.success
    +
    +
    +
    +def issue_refund(self, payment_id, amount=-1) +
    +
    +

    Issue Refund. +It is used if refund needed even after successfull payment.

    +

    Args

    +
    +
    payment_id : str
    +
    payment id of the payment needs to be refunded.
    +
    amount : int, optional
    +
    amount of the refund value. Defaults to -1(Meaning refund the total amount of the payment).
    +
    +
    + +Expand source code + +
    def issue_refund(self, payment_id, amount=-1):
    +    """Issue Refund.
    +    It is used if refund needed even after successfull payment.
    +    Args:
    +        payment_id (str): payment id of the payment needs to be refunded.
    +        amount (int, optional): amount of the refund value. Defaults to -1(Meaning refund the total amount of the payment).
    +    """
    +    instance_name = f"refund_{payment_id}"
    +    request = REFUND_FACTORY.new(instance_name, payment_id=payment_id, amount=amount)
    +    request.save()
    +    j.logger.info(f"refund request created for payment: {payment_id}")
    +    return
    +
    +
    +
    +def process_payments(self) +
    +
    +

    Process any active payment. +list all active payment and apply it.

    +
    + +Expand source code + +
    def process_payments(self):
    +    """Process any active payment.
    +    list all active payment and apply it.
    +    """
    +    for payment in PAYMENT_FACTORY.list_active_payments():
    +        j.logger.info(f"updating active payment: {payment.payment_id}")
    +        payment.update_status()
    +
    +
    +
    +def process_refunds(self) +
    +
    +

    Process any active refund. +list all active refund and apply it.

    +
    + +Expand source code + +
    def process_refunds(self):
    +    """Process any active refund.
    +    list all active refund and apply it.
    +    """
    +    for request in REFUND_FACTORY.list_active_requests():
    +        j.logger.info(f"applying active refund for payment: {request.payment_id}")
    +        request.apply()
    +
    +
    +
    +def refund_extra(self) +
    +
    +

    Process any active extra refund. +list all active extra refund and apply it.

    +
    + +Expand source code + +
    def refund_extra(self):
    +    """Process any active extra refund.
    +    list all active extra refund and apply it.
    +    """
    +    for payment in PAYMENT_FACTORY.list_extra_paid_payments():
    +        j.logger.info(f"refund extra paid for payment: {payment.payment_id}")
    +        payment.result.refund_extra()
    +
    +
    +
    +def refund_failed_payments(self) +
    +
    +

    Refund any failed payment. +Example: transfer amount less than payment amount.

    +
    + +Expand source code + +
    def refund_failed_payments(self):
    +    """Refund any failed payment.
    +    Example: transfer amount less than payment amount.
    +    """
    +    for payment in PAYMENT_FACTORY.list_failed_payments():
    +        refund_result = True
    +        for transaction in payment.result.transactions:
    +            if transaction.success or transaction.transaction_refund.success:
    +                continue
    +            j.logger.info(f"refunding transaction: {transaction.transaction_hash} of payment: {payment.payment_id}")
    +            refund_result = refund_result and transaction.refund(payment.wallet)
    +            payment.save()
    +
    +
    +
    +def submit_payment(self, amount, wallet_name, refund_extra=True, expiry=5, description='') +
    +
    +

    Submit payment.

    +

    Args

    +
    +
    amount : float
    +
    amount of current payment.
    +
    wallet_name : str
    +
    name of wallet that submit the payment.
    +
    refund_extra : bool, optional
    +
    if the payment has extra amount it will be refunded. Defaults to True.
    +
    expiry : int, optional
    +
    time in minutes the payment will expire after it. Defaults to 5.
    +
    description : str, optional
    +
    describe the payment. Defaults to "".
    +
    +

    Returns

    +

    tuple(str, str): payment_id and memo_text that used as confirmation for the payment.

    +
    + +Expand source code + +
    def submit_payment(self, amount, wallet_name, refund_extra=True, expiry=5, description=""):
    +    """Submit payment.
    +
    +    Args:
    +        amount (float): amount of current payment.
    +        wallet_name (str): name of wallet that submit the payment.
    +        refund_extra (bool, optional): if the payment has extra amount it will be refunded. Defaults to True.
    +        expiry (int, optional): time in minutes the payment will expire after it. Defaults to 5.
    +        description (str, optional): describe the payment. Defaults to "".
    +
    +    Returns:
    +        tuple(str, str): payment_id and memo_text that used as confirmation for the payment.
    +    """
    +    payment_id = uuid.uuid4().hex
    +    instance_name = f"payment_{payment_id}"
    +    payment = PAYMENT_FACTORY.new(
    +        instance_name,
    +        payment_id=payment_id,
    +        amount=round(amount, 6),
    +        wallet_name=wallet_name,
    +        refund_extra=refund_extra,
    +        description=description,
    +    )
    +    payment.deadline = datetime.datetime.utcnow() + datetime.timedelta(minutes=expiry)
    +    payment.save()
    +    j.logger.info(
    +        f"payment {payment_id} submitted for wallet: {wallet_name} amount: {amount}, description: {description}, expiry: {expiry}, memo_text: {payment.memo_text}"
    +    )
    +    return payment_id, payment.memo_text
    +
    +
    +
    +def wait_payment(self, payment_id, bot=None, notes=None) +
    +
    +

    Wait payment amount of time.

    +

    Args

    +
    +
    payment_id : str
    +
    payment id to wait on.
    +
    bot : used in case of using it with threebot deployer or VDC chatflow. Defaults to None.
    +
    notes : str, optional
    +
    optional note that appears while waiting. Defaults to None.
    +
    +

    Returns

    +
    +
    bool
    +
    return result of this payment, True if payment finished, False if payment expired.
    +
    +
    + +Expand source code + +
    def wait_payment(self, payment_id, bot=None, notes=None):
    +    """Wait payment amount of time.
    +
    +    Args:
    +        payment_id (str): payment id to wait on.
    +        bot : used in case of using it with threebot deployer or VDC chatflow. Defaults to None.
    +        notes (str, optional): optional note that appears while waiting. Defaults to None.
    +
    +    Returns:
    +        bool: return result of this payment, True if payment finished, False if payment expired.
    +    """
    +    j.logger.info(f"waiting payment: {payment_id}")
    +    payment = PAYMENT_FACTORY.find_by_id(payment_id)
    +    if bot:
    +        self._show_payment(bot, payment, notes)
    +
    +    while not payment.is_finished():
    +        gevent.sleep(3)
    +        payment = PAYMENT_FACTORY.find_by_id(payment_id)
    +    j.logger.info(f"payment: {payment_id} result {payment.result.success}")
    +    return payment.result.success
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/billing/index.html b/docs/api/jumpscale/sals/billing/index.html new file mode 100644 index 000000000..879c52bc8 --- /dev/null +++ b/docs/api/jumpscale/sals/billing/index.html @@ -0,0 +1,103 @@ + + + + + + +jumpscale.sals.billing API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.billing

    +
    +
    +
    + +Expand source code + +
    from .billing import BillingManager
    +
    +BILLING_MANGER = BillingManager()
    +
    +
    +def export_module_as():
    +    return BILLING_MANGER
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.sals.billing.billing
    +
    +
    +
    +
    jumpscale.sals.billing.models
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    return BILLING_MANGER
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/billing/models.html b/docs/api/jumpscale/sals/billing/models.html new file mode 100644 index 000000000..ca414d0cc --- /dev/null +++ b/docs/api/jumpscale/sals/billing/models.html @@ -0,0 +1,1887 @@ + + + + + + +jumpscale.sals.billing.models API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.billing.models

    +
    +
    +
    + +Expand source code + +
    from jumpscale.core.base import Base, fields, StoredFactory
    +import datetime
    +from jumpscale.loader import j
    +from jumpscale.clients.stellar import TRANSACTION_FEES
    +from decimal import Decimal
    +import datetime
    +
    +
    +class PaymentTransactionRefund(Base):
    +    refund_transaction_hash = fields.String()
    +    success = fields.Boolean(default=False)
    +
    +
    +class PaymentTransaction(Base):
    +    transaction_hash = fields.String(required=True)
    +    transaction_refund = fields.Object(PaymentTransactionRefund)
    +    success = fields.Boolean(default=False)
    +
    +    def refund(self, wallet):
    +        if self.transaction_refund.success:
    +            return True
    +        try:
    +            amount = round(self.get_amount(wallet) - Decimal(TRANSACTION_FEES), 6)
    +            if amount < 0:
    +                self.transaction_refund.success = True
    +            else:
    +                a = wallet._get_asset()
    +                sender_address = wallet.get_sender_wallet_address(self.transaction_hash)
    +                j.logger.info(
    +                    f"refunding transaction: {self.transaction_hash} with amount: {amount} to address: {sender_address}"
    +                )
    +                self.transaction_refund.transaction_hash = wallet.transfer(
    +                    sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
    +                )
    +                self.transaction_refund.success = True
    +                j.logger.info(
    +                    f"transaction: {self.transaction_hash} refunded successfully with amount: {amount} to address: {sender_address} in transaction: {self.transaction_refund.transaction_hash}"
    +                )
    +        except Exception as e:
    +            j.logger.critical(f"failed to refund transaction: {self.transaction_hash} due to error: {str(e)}")
    +        return self.transaction_refund.success
    +
    +    def get_amount(self, wallet):
    +        try:
    +            effects = wallet.get_transaction_effects(self.transaction_hash)
    +        except Exception as e:
    +            j.logger.warning(f"failed to get transaction effects of hash {self.transaction_hash} due to error {str(e)}")
    +            raise e
    +        trans_amount = 0
    +        for effect in effects:
    +            if effect.asset_code != "TFT":
    +                continue
    +            trans_amount += effect.amount
    +        return trans_amount
    +
    +
    +class PaymentResult(Base):
    +    success = fields.Boolean(default=False)
    +    extra_paid = fields.Boolean(default=False)
    +    transactions = fields.List(fields.Object(PaymentTransaction))
    +
    +    def refund_extra(self):
    +        if self.extra_paid and self.parent.refund_extra:
    +            for transaction in self.transactions:
    +                if transaction.success:
    +                    trans_amount = transaction.get_amount(self.parent.wallet)
    +                    diff = float(trans_amount) - self.parent.amount
    +                    if diff <= TRANSACTION_FEES:
    +                        self.extra_paid = False
    +                        break
    +                    sender_address = self.parent.wallet.get_sender_wallet_address(transaction.transaction_hash)
    +                    amount = round(diff - TRANSACTION_FEES, 6)
    +                    try:
    +                        j.logger.info(
    +                            f"refunding extra amount: {amount} of transaction {transaction.transaction_hash} to address: {sender_address}"
    +                        )
    +                        a = self.parent.wallet._get_asset()
    +                        refund_hash = self.parent.wallet.transfer(
    +                            sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
    +                        )
    +                        self.extra_paid = False
    +                        j.logger.info(
    +                            f"extra amount: {amount} of transaction {transaction.transaction_hash} refunded successfully in transaction: {refund_hash} to address: {sender_address}"
    +                        )
    +                    except Exception as e:
    +                        j.logger.critical(
    +                            f"failed to refund extra amount {amount} for payment: {self.parent.payment_id} due to error: {str(e)}"
    +                        )
    +            self.parent.save()
    +        return self.extra_paid
    +
    +
    +class Payment(Base):
    +    payment_id = fields.String()
    +    wallet_name = fields.String(required=True)
    +    amount = fields.Float(required=True)
    +    memo_text = fields.String(default=lambda: j.data.idgenerator.chars(28))
    +    created_at = fields.DateTime(default=datetime.datetime.utcnow)
    +    deadline = fields.DateTime(default=lambda: datetime.datetime.utcnow() + datetime.timedelta(minutes=5))
    +    result = fields.Object(PaymentResult, required=True)
    +    refund_extra = fields.Boolean(default=True)
    +    description = fields.String()
    +
    +    def is_finished(self):
    +        if self.deadline.timestamp() < j.data.time.utcnow().timestamp or self.result.success:
    +            return True
    +
    +        return False
    +
    +    @property
    +    def wallet(self):
    +        return j.clients.stellar.get(self.wallet_name)
    +
    +    def update_status(self):
    +        if self.is_finished():
    +            return
    +        if self.amount == 0:
    +            self.result.success = True
    +            self.save()
    +            return
    +        j.logger.info(f"updating payment: {self.payment_id} status")
    +        transactions = self.wallet.list_transactions()
    +        current_transactions = {t.transaction_hash: t for t in self.result.transactions}
    +        for transaction in transactions:
    +            transaction_hash = transaction.hash
    +            if transaction_hash in current_transactions:
    +                continue
    +            trans_memo_text = transaction.memo_text
    +            if not trans_memo_text:
    +                continue
    +
    +            if trans_memo_text != self.memo_text:
    +                continue
    +
    +            j.logger.info(f"adding transaction {transaction_hash} to payment: {self.payment_id}")
    +
    +            trans_obj = PaymentTransaction()
    +            trans_obj.transaction_hash = transaction_hash
    +            self.result.transactions.append(trans_obj)
    +
    +            if not self.result.success:
    +                try:
    +                    trans_amount = trans_obj.get_amount(self.wallet)
    +                    j.logger.info(
    +                        f"adding transaction {transaction_hash} to payment: {self.payment_id} with amount: {trans_amount}"
    +                    )
    +                except Exception as e:
    +                    j.logger.error(
    +                        f"failed to update payment {self.instance_name} with transaction {transaction_hash} due to error {str(e)}"
    +                    )
    +                    continue
    +                if trans_amount >= Decimal(self.amount) or abs(trans_amount - Decimal(self.amount)) <= 0.000001:
    +                    j.logger.info(
    +                        f"payment: {self.payment_id} fulfilled by transaction: {transaction_hash} with amount: {trans_amount}"
    +                    )
    +                    trans_obj.success = True
    +                    self.result.success = True
    +                    if trans_amount > Decimal(self.amount):
    +                        j.logger.info(f"payment: {self.payment_id} is marked as extra paid")
    +                        self.result.extra_paid = True
    +            self.save()
    +
    +
    +class PaymentFactory(StoredFactory):
    +    def find_by_id(self, payment_id):
    +        instance_name = f"payment_{payment_id}"
    +        return self.find(instance_name)
    +
    +    def list_failed_payments(self):
    +        for name in self.list_all():
    +            payment = self.find(name)
    +            payment.update_status()
    +            if payment.is_finished():
    +                if not payment.result.success:
    +                    yield payment
    +                else:
    +                    for transaction in payment.result.transactions:
    +                        if not transaction.success and not transaction.transaction_refund.success:
    +                            yield payment
    +                            break
    +
    +    def list_active_payments(self):
    +        for name in self.list_all():
    +            payment = self.find(name)
    +            if not payment.is_finished():
    +                yield payment
    +
    +    def list_extra_paid_payments(self):
    +        _, _, payments = self.find_many(refund_extra=True)
    +        for payment in payments:
    +            if payment.result.extra_paid and payment.is_finished():
    +                yield payment
    +
    +
    +PAYMENT_FACTORY = PaymentFactory(Payment)
    +PAYMENT_FACTORY.always_reload = True
    +
    +
    +class RefundRequest(Base):
    +    payment_id = fields.String(required=True)
    +    success = fields.Boolean(default=False)
    +    refund_transaction_hash = fields.String()
    +    last_tried = fields.DateTime()
    +    amount = fields.Float(default=-1)
    +
    +    def apply(self):
    +        payment = PAYMENT_FACTORY.find_by_id(self.payment_id)
    +        if not payment.is_finished():
    +            j.logger.warning(f"can't refund active payment {self.payment_id}")
    +            return False
    +
    +        self.last_tried = datetime.datetime.utcnow()
    +        amount = payment.amount
    +        # check if refund extra is False. then amount should be same as successful transaction in case of extra was paid but not refunded automatically
    +        sender_address = None
    +        for transaction in payment.result.transactions:
    +            if transaction.success:
    +                sender_address = payment.wallet.get_sender_wallet_address(transaction.transaction_hash)
    +                if not payment.refund_extra:
    +                    amount = float(transaction.get_amount(payment.wallet))
    +
    +        # if a specific amount was specified by the refund request
    +        if self.amount > 0:
    +            amount = self.amount
    +
    +        if amount <= TRANSACTION_FEES or not sender_address:
    +            self.success = True
    +        else:
    +            try:
    +                a = payment.wallet._get_asset()
    +                self.refund_transaction_hash = payment.wallet.transfer(
    +                    sender_address, amount=round(amount - TRANSACTION_FEES, 6), asset=f"{a.code}:{a.issuer}"
    +                )
    +                self.success = True
    +                j.logger.info(
    +                    f"refund request successful for payment: {self.payment_id} amount: {amount} to address: {sender_address} in transaction: {self.refund_transaction_hash}"
    +                )
    +            except Exception as e:
    +                j.logger.critical(f"failed to apply refund request for payment {self.payment_id} due to error {str(e)}")
    +        self.save()
    +        return self.success
    +
    +
    +class RefundFactory(StoredFactory):
    +    def list_active_requests(self):
    +        _, _, refunds = self.find_many(success=False)
    +        return refunds
    +
    +
    +REFUND_FACTORY = RefundFactory(RefundRequest)
    +REFUND_FACTORY.always_reload = True
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Payment +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class Payment(Base):
    +    payment_id = fields.String()
    +    wallet_name = fields.String(required=True)
    +    amount = fields.Float(required=True)
    +    memo_text = fields.String(default=lambda: j.data.idgenerator.chars(28))
    +    created_at = fields.DateTime(default=datetime.datetime.utcnow)
    +    deadline = fields.DateTime(default=lambda: datetime.datetime.utcnow() + datetime.timedelta(minutes=5))
    +    result = fields.Object(PaymentResult, required=True)
    +    refund_extra = fields.Boolean(default=True)
    +    description = fields.String()
    +
    +    def is_finished(self):
    +        if self.deadline.timestamp() < j.data.time.utcnow().timestamp or self.result.success:
    +            return True
    +
    +        return False
    +
    +    @property
    +    def wallet(self):
    +        return j.clients.stellar.get(self.wallet_name)
    +
    +    def update_status(self):
    +        if self.is_finished():
    +            return
    +        if self.amount == 0:
    +            self.result.success = True
    +            self.save()
    +            return
    +        j.logger.info(f"updating payment: {self.payment_id} status")
    +        transactions = self.wallet.list_transactions()
    +        current_transactions = {t.transaction_hash: t for t in self.result.transactions}
    +        for transaction in transactions:
    +            transaction_hash = transaction.hash
    +            if transaction_hash in current_transactions:
    +                continue
    +            trans_memo_text = transaction.memo_text
    +            if not trans_memo_text:
    +                continue
    +
    +            if trans_memo_text != self.memo_text:
    +                continue
    +
    +            j.logger.info(f"adding transaction {transaction_hash} to payment: {self.payment_id}")
    +
    +            trans_obj = PaymentTransaction()
    +            trans_obj.transaction_hash = transaction_hash
    +            self.result.transactions.append(trans_obj)
    +
    +            if not self.result.success:
    +                try:
    +                    trans_amount = trans_obj.get_amount(self.wallet)
    +                    j.logger.info(
    +                        f"adding transaction {transaction_hash} to payment: {self.payment_id} with amount: {trans_amount}"
    +                    )
    +                except Exception as e:
    +                    j.logger.error(
    +                        f"failed to update payment {self.instance_name} with transaction {transaction_hash} due to error {str(e)}"
    +                    )
    +                    continue
    +                if trans_amount >= Decimal(self.amount) or abs(trans_amount - Decimal(self.amount)) <= 0.000001:
    +                    j.logger.info(
    +                        f"payment: {self.payment_id} fulfilled by transaction: {transaction_hash} with amount: {trans_amount}"
    +                    )
    +                    trans_obj.success = True
    +                    self.result.success = True
    +                    if trans_amount > Decimal(self.amount):
    +                        j.logger.info(f"payment: {self.payment_id} is marked as extra paid")
    +                        self.result.extra_paid = True
    +            self.save()
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var amount
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var created_at
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var deadline
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var description
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var memo_text
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var payment_id
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var refund_extra
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var result
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var wallet
    +
    +
    +
    + +Expand source code + +
    @property
    +def wallet(self):
    +    return j.clients.stellar.get(self.wallet_name)
    +
    +
    +
    var wallet_name
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def is_finished(self) +
    +
    +
    +
    + +Expand source code + +
    def is_finished(self):
    +    if self.deadline.timestamp() < j.data.time.utcnow().timestamp or self.result.success:
    +        return True
    +
    +    return False
    +
    +
    +
    +def update_status(self) +
    +
    +
    +
    + +Expand source code + +
    def update_status(self):
    +    if self.is_finished():
    +        return
    +    if self.amount == 0:
    +        self.result.success = True
    +        self.save()
    +        return
    +    j.logger.info(f"updating payment: {self.payment_id} status")
    +    transactions = self.wallet.list_transactions()
    +    current_transactions = {t.transaction_hash: t for t in self.result.transactions}
    +    for transaction in transactions:
    +        transaction_hash = transaction.hash
    +        if transaction_hash in current_transactions:
    +            continue
    +        trans_memo_text = transaction.memo_text
    +        if not trans_memo_text:
    +            continue
    +
    +        if trans_memo_text != self.memo_text:
    +            continue
    +
    +        j.logger.info(f"adding transaction {transaction_hash} to payment: {self.payment_id}")
    +
    +        trans_obj = PaymentTransaction()
    +        trans_obj.transaction_hash = transaction_hash
    +        self.result.transactions.append(trans_obj)
    +
    +        if not self.result.success:
    +            try:
    +                trans_amount = trans_obj.get_amount(self.wallet)
    +                j.logger.info(
    +                    f"adding transaction {transaction_hash} to payment: {self.payment_id} with amount: {trans_amount}"
    +                )
    +            except Exception as e:
    +                j.logger.error(
    +                    f"failed to update payment {self.instance_name} with transaction {transaction_hash} due to error {str(e)}"
    +                )
    +                continue
    +            if trans_amount >= Decimal(self.amount) or abs(trans_amount - Decimal(self.amount)) <= 0.000001:
    +                j.logger.info(
    +                    f"payment: {self.payment_id} fulfilled by transaction: {transaction_hash} with amount: {trans_amount}"
    +                )
    +                trans_obj.success = True
    +                self.result.success = True
    +                if trans_amount > Decimal(self.amount):
    +                    j.logger.info(f"payment: {self.payment_id} is marked as extra paid")
    +                    self.result.extra_paid = True
    +        self.save()
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class PaymentFactory +(type_, name_=None, parent_instance_=None, parent_factory_=None) +
    +
    +

    Stored factories are a custom type of Factory, which uses current configured store backend +to store all instance configurations.

    +

    get a new stored factory given the type to create and store instances for.

    +

    Any factory can have a name, parent Base instance and a parent factory.

    +

    Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

    +

    Args

    +
    +
    type_ : Base
    +
    Base class type
    +
    name_ : str, optional
    +
    factory name. Defaults to None.
    +
    parent_instance_ : Base, optional
    +
    a parent Base instance. Defaults to None.
    +
    parent_factory_ : Factory, optional
    +
    a parent Factory. Defaults to None.
    +
    +
    + +Expand source code + +
    class PaymentFactory(StoredFactory):
    +    def find_by_id(self, payment_id):
    +        instance_name = f"payment_{payment_id}"
    +        return self.find(instance_name)
    +
    +    def list_failed_payments(self):
    +        for name in self.list_all():
    +            payment = self.find(name)
    +            payment.update_status()
    +            if payment.is_finished():
    +                if not payment.result.success:
    +                    yield payment
    +                else:
    +                    for transaction in payment.result.transactions:
    +                        if not transaction.success and not transaction.transaction_refund.success:
    +                            yield payment
    +                            break
    +
    +    def list_active_payments(self):
    +        for name in self.list_all():
    +            payment = self.find(name)
    +            if not payment.is_finished():
    +                yield payment
    +
    +    def list_extra_paid_payments(self):
    +        _, _, payments = self.find_many(refund_extra=True)
    +        for payment in payments:
    +            if payment.result.extra_paid and payment.is_finished():
    +                yield payment
    +
    +

    Ancestors

    + +

    Subclasses

    +
      +
    • jumpscale.core.base.factory.PaymentFactory
    • +
    +

    Methods

    +
    +
    +def find_by_id(self, payment_id) +
    +
    +
    +
    + +Expand source code + +
    def find_by_id(self, payment_id):
    +    instance_name = f"payment_{payment_id}"
    +    return self.find(instance_name)
    +
    +
    +
    +def list_active_payments(self) +
    +
    +
    +
    + +Expand source code + +
    def list_active_payments(self):
    +    for name in self.list_all():
    +        payment = self.find(name)
    +        if not payment.is_finished():
    +            yield payment
    +
    +
    +
    +def list_extra_paid_payments(self) +
    +
    +
    +
    + +Expand source code + +
    def list_extra_paid_payments(self):
    +    _, _, payments = self.find_many(refund_extra=True)
    +    for payment in payments:
    +        if payment.result.extra_paid and payment.is_finished():
    +            yield payment
    +
    +
    +
    +def list_failed_payments(self) +
    +
    +
    +
    + +Expand source code + +
    def list_failed_payments(self):
    +    for name in self.list_all():
    +        payment = self.find(name)
    +        payment.update_status()
    +        if payment.is_finished():
    +            if not payment.result.success:
    +                yield payment
    +            else:
    +                for transaction in payment.result.transactions:
    +                    if not transaction.success and not transaction.transaction_refund.success:
    +                        yield payment
    +                        break
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class PaymentResult +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class PaymentResult(Base):
    +    success = fields.Boolean(default=False)
    +    extra_paid = fields.Boolean(default=False)
    +    transactions = fields.List(fields.Object(PaymentTransaction))
    +
    +    def refund_extra(self):
    +        if self.extra_paid and self.parent.refund_extra:
    +            for transaction in self.transactions:
    +                if transaction.success:
    +                    trans_amount = transaction.get_amount(self.parent.wallet)
    +                    diff = float(trans_amount) - self.parent.amount
    +                    if diff <= TRANSACTION_FEES:
    +                        self.extra_paid = False
    +                        break
    +                    sender_address = self.parent.wallet.get_sender_wallet_address(transaction.transaction_hash)
    +                    amount = round(diff - TRANSACTION_FEES, 6)
    +                    try:
    +                        j.logger.info(
    +                            f"refunding extra amount: {amount} of transaction {transaction.transaction_hash} to address: {sender_address}"
    +                        )
    +                        a = self.parent.wallet._get_asset()
    +                        refund_hash = self.parent.wallet.transfer(
    +                            sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
    +                        )
    +                        self.extra_paid = False
    +                        j.logger.info(
    +                            f"extra amount: {amount} of transaction {transaction.transaction_hash} refunded successfully in transaction: {refund_hash} to address: {sender_address}"
    +                        )
    +                    except Exception as e:
    +                        j.logger.critical(
    +                            f"failed to refund extra amount {amount} for payment: {self.parent.payment_id} due to error: {str(e)}"
    +                        )
    +            self.parent.save()
    +        return self.extra_paid
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var extra_paid
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var success
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var transactions
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def refund_extra(self) +
    +
    +
    +
    + +Expand source code + +
    def refund_extra(self):
    +    if self.extra_paid and self.parent.refund_extra:
    +        for transaction in self.transactions:
    +            if transaction.success:
    +                trans_amount = transaction.get_amount(self.parent.wallet)
    +                diff = float(trans_amount) - self.parent.amount
    +                if diff <= TRANSACTION_FEES:
    +                    self.extra_paid = False
    +                    break
    +                sender_address = self.parent.wallet.get_sender_wallet_address(transaction.transaction_hash)
    +                amount = round(diff - TRANSACTION_FEES, 6)
    +                try:
    +                    j.logger.info(
    +                        f"refunding extra amount: {amount} of transaction {transaction.transaction_hash} to address: {sender_address}"
    +                    )
    +                    a = self.parent.wallet._get_asset()
    +                    refund_hash = self.parent.wallet.transfer(
    +                        sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
    +                    )
    +                    self.extra_paid = False
    +                    j.logger.info(
    +                        f"extra amount: {amount} of transaction {transaction.transaction_hash} refunded successfully in transaction: {refund_hash} to address: {sender_address}"
    +                    )
    +                except Exception as e:
    +                    j.logger.critical(
    +                        f"failed to refund extra amount {amount} for payment: {self.parent.payment_id} due to error: {str(e)}"
    +                    )
    +        self.parent.save()
    +    return self.extra_paid
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class PaymentTransaction +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class PaymentTransaction(Base):
    +    transaction_hash = fields.String(required=True)
    +    transaction_refund = fields.Object(PaymentTransactionRefund)
    +    success = fields.Boolean(default=False)
    +
    +    def refund(self, wallet):
    +        if self.transaction_refund.success:
    +            return True
    +        try:
    +            amount = round(self.get_amount(wallet) - Decimal(TRANSACTION_FEES), 6)
    +            if amount < 0:
    +                self.transaction_refund.success = True
    +            else:
    +                a = wallet._get_asset()
    +                sender_address = wallet.get_sender_wallet_address(self.transaction_hash)
    +                j.logger.info(
    +                    f"refunding transaction: {self.transaction_hash} with amount: {amount} to address: {sender_address}"
    +                )
    +                self.transaction_refund.transaction_hash = wallet.transfer(
    +                    sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
    +                )
    +                self.transaction_refund.success = True
    +                j.logger.info(
    +                    f"transaction: {self.transaction_hash} refunded successfully with amount: {amount} to address: {sender_address} in transaction: {self.transaction_refund.transaction_hash}"
    +                )
    +        except Exception as e:
    +            j.logger.critical(f"failed to refund transaction: {self.transaction_hash} due to error: {str(e)}")
    +        return self.transaction_refund.success
    +
    +    def get_amount(self, wallet):
    +        try:
    +            effects = wallet.get_transaction_effects(self.transaction_hash)
    +        except Exception as e:
    +            j.logger.warning(f"failed to get transaction effects of hash {self.transaction_hash} due to error {str(e)}")
    +            raise e
    +        trans_amount = 0
    +        for effect in effects:
    +            if effect.asset_code != "TFT":
    +                continue
    +            trans_amount += effect.amount
    +        return trans_amount
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var success
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var transaction_hash
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var transaction_refund
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def get_amount(self, wallet) +
    +
    +
    +
    + +Expand source code + +
    def get_amount(self, wallet):
    +    try:
    +        effects = wallet.get_transaction_effects(self.transaction_hash)
    +    except Exception as e:
    +        j.logger.warning(f"failed to get transaction effects of hash {self.transaction_hash} due to error {str(e)}")
    +        raise e
    +    trans_amount = 0
    +    for effect in effects:
    +        if effect.asset_code != "TFT":
    +            continue
    +        trans_amount += effect.amount
    +    return trans_amount
    +
    +
    +
    +def refund(self, wallet) +
    +
    +
    +
    + +Expand source code + +
    def refund(self, wallet):
    +    if self.transaction_refund.success:
    +        return True
    +    try:
    +        amount = round(self.get_amount(wallet) - Decimal(TRANSACTION_FEES), 6)
    +        if amount < 0:
    +            self.transaction_refund.success = True
    +        else:
    +            a = wallet._get_asset()
    +            sender_address = wallet.get_sender_wallet_address(self.transaction_hash)
    +            j.logger.info(
    +                f"refunding transaction: {self.transaction_hash} with amount: {amount} to address: {sender_address}"
    +            )
    +            self.transaction_refund.transaction_hash = wallet.transfer(
    +                sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
    +            )
    +            self.transaction_refund.success = True
    +            j.logger.info(
    +                f"transaction: {self.transaction_hash} refunded successfully with amount: {amount} to address: {sender_address} in transaction: {self.transaction_refund.transaction_hash}"
    +            )
    +    except Exception as e:
    +        j.logger.critical(f"failed to refund transaction: {self.transaction_hash} due to error: {str(e)}")
    +    return self.transaction_refund.success
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class PaymentTransactionRefund +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class PaymentTransactionRefund(Base):
    +    refund_transaction_hash = fields.String()
    +    success = fields.Boolean(default=False)
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var refund_transaction_hash
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var success
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class RefundFactory +(type_, name_=None, parent_instance_=None, parent_factory_=None) +
    +
    +

    Stored factories are a custom type of Factory, which uses current configured store backend +to store all instance configurations.

    +

    get a new stored factory given the type to create and store instances for.

    +

    Any factory can have a name, parent Base instance and a parent factory.

    +

    Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

    +

    Args

    +
    +
    type_ : Base
    +
    Base class type
    +
    name_ : str, optional
    +
    factory name. Defaults to None.
    +
    parent_instance_ : Base, optional
    +
    a parent Base instance. Defaults to None.
    +
    parent_factory_ : Factory, optional
    +
    a parent Factory. Defaults to None.
    +
    +
    + +Expand source code + +
    class RefundFactory(StoredFactory):
    +    def list_active_requests(self):
    +        _, _, refunds = self.find_many(success=False)
    +        return refunds
    +
    +

    Ancestors

    + +

    Subclasses

    +
      +
    • jumpscale.core.base.factory.RefundFactory
    • +
    +

    Methods

    +
    +
    +def list_active_requests(self) +
    +
    +
    +
    + +Expand source code + +
    def list_active_requests(self):
    +    _, _, refunds = self.find_many(success=False)
    +    return refunds
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class RefundRequest +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class RefundRequest(Base):
    +    payment_id = fields.String(required=True)
    +    success = fields.Boolean(default=False)
    +    refund_transaction_hash = fields.String()
    +    last_tried = fields.DateTime()
    +    amount = fields.Float(default=-1)
    +
    +    def apply(self):
    +        payment = PAYMENT_FACTORY.find_by_id(self.payment_id)
    +        if not payment.is_finished():
    +            j.logger.warning(f"can't refund active payment {self.payment_id}")
    +            return False
    +
    +        self.last_tried = datetime.datetime.utcnow()
    +        amount = payment.amount
    +        # check if refund extra is False. then amount should be same as successful transaction in case of extra was paid but not refunded automatically
    +        sender_address = None
    +        for transaction in payment.result.transactions:
    +            if transaction.success:
    +                sender_address = payment.wallet.get_sender_wallet_address(transaction.transaction_hash)
    +                if not payment.refund_extra:
    +                    amount = float(transaction.get_amount(payment.wallet))
    +
    +        # if a specific amount was specified by the refund request
    +        if self.amount > 0:
    +            amount = self.amount
    +
    +        if amount <= TRANSACTION_FEES or not sender_address:
    +            self.success = True
    +        else:
    +            try:
    +                a = payment.wallet._get_asset()
    +                self.refund_transaction_hash = payment.wallet.transfer(
    +                    sender_address, amount=round(amount - TRANSACTION_FEES, 6), asset=f"{a.code}:{a.issuer}"
    +                )
    +                self.success = True
    +                j.logger.info(
    +                    f"refund request successful for payment: {self.payment_id} amount: {amount} to address: {sender_address} in transaction: {self.refund_transaction_hash}"
    +                )
    +            except Exception as e:
    +                j.logger.critical(f"failed to apply refund request for payment {self.payment_id} due to error {str(e)}")
    +        self.save()
    +        return self.success
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var amount
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var last_tried
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var payment_id
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var refund_transaction_hash
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var success
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def apply(self) +
    +
    +
    +
    + +Expand source code + +
    def apply(self):
    +    payment = PAYMENT_FACTORY.find_by_id(self.payment_id)
    +    if not payment.is_finished():
    +        j.logger.warning(f"can't refund active payment {self.payment_id}")
    +        return False
    +
    +    self.last_tried = datetime.datetime.utcnow()
    +    amount = payment.amount
    +    # check if refund extra is False. then amount should be same as successful transaction in case of extra was paid but not refunded automatically
    +    sender_address = None
    +    for transaction in payment.result.transactions:
    +        if transaction.success:
    +            sender_address = payment.wallet.get_sender_wallet_address(transaction.transaction_hash)
    +            if not payment.refund_extra:
    +                amount = float(transaction.get_amount(payment.wallet))
    +
    +    # if a specific amount was specified by the refund request
    +    if self.amount > 0:
    +        amount = self.amount
    +
    +    if amount <= TRANSACTION_FEES or not sender_address:
    +        self.success = True
    +    else:
    +        try:
    +            a = payment.wallet._get_asset()
    +            self.refund_transaction_hash = payment.wallet.transfer(
    +                sender_address, amount=round(amount - TRANSACTION_FEES, 6), asset=f"{a.code}:{a.issuer}"
    +            )
    +            self.success = True
    +            j.logger.info(
    +                f"refund request successful for payment: {self.payment_id} amount: {amount} to address: {sender_address} in transaction: {self.refund_transaction_hash}"
    +            )
    +        except Exception as e:
    +            j.logger.critical(f"failed to apply refund request for payment {self.payment_id} due to error {str(e)}")
    +    self.save()
    +    return self.success
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/chatflows/chatflows.html b/docs/api/jumpscale/sals/chatflows/chatflows.html new file mode 100644 index 000000000..3f893c9b3 --- /dev/null +++ b/docs/api/jumpscale/sals/chatflows/chatflows.html @@ -0,0 +1,2910 @@ + + + + + + +jumpscale.sals.chatflows.chatflows API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.chatflows.chatflows

    +
    +
    +
    + +Expand source code + +
    import base64
    +import sys
    +import uuid
    +from importlib import import_module
    +import inspect
    +import json
    +import gevent
    +import gevent.queue
    +import html
    +from jumpscale.loader import j
    +import stellar_sdk
    +
    +
    +class Result:
    +    def __init__(self, loader=str):
    +        self._value = None
    +        self._loader = loader
    +
    +    @property
    +    def value(self):
    +        return self._value
    +
    +    @value.setter
    +    def value(self, value):
    +        self._value = self._loader(value)
    +
    +
    +class Form:
    +    def __init__(self, session):
    +        self._session = session
    +        self.fields = []
    +        self.results = []
    +
    +    def ask(self, msg=None, **kwargs):
    +        self._session.send_data(
    +            {"category": "form", "msg": msg, "fields": self.fields, "kwargs": kwargs}, is_slide=True
    +        )
    +        results = j.data.serializers.json.loads(self._session._queue_in.get())
    +        for result, resobject in zip(results, self.results):
    +            resobject.value = result
    +
    +    def _append(self, msg, loader=str):
    +        self.fields.append(msg)
    +        result = Result(loader)
    +        self.results.append(result)
    +        return result
    +
    +    def string_ask(self, msg, **kwargs):
    +        return self._append(self._session.string_msg(msg, **kwargs))
    +
    +    def int_ask(self, msg, **kwargs):
    +        return self._append(self._session.int_msg(msg, **kwargs), int)
    +
    +    def secret_ask(self, msg, **kwargs):
    +        return self._append(self._session.secret_msg(msg, **kwargs))
    +
    +    def datetime_picker(self, msg, **kwargs):
    +        return self._append(self._session.datetime_picker_msg(msg, **kwargs))
    +
    +    def multi_list_choice(self, msg, options, **kwargs):
    +        return self._append(self._session.multi_list_choice_msg(msg, options, **kwargs))
    +
    +    def upload_file(self, msg, **kwargs):
    +        return self._append(self._session.upload_file_msg(msg, **kwargs))
    +
    +    def multi_choice(self, msg, options, **kwargs):
    +        return self._append(self._session.multi_choice_msg(msg, options, **kwargs), j.data.serializers.json.loads)
    +
    +    def single_choice(self, msg, options, **kwargs):
    +        return self._append(self._session.single_choice_msg(msg, options, **kwargs))
    +
    +    def drop_down_choice(self, msg, options, **kwargs):
    +        return self._append(self._session.drop_down_choice_msg(msg, options, **kwargs))
    +
    +
    +class GedisChatBot:
    +    """
    +    Contains the basic helper methods for asking questions
    +    It also have the main queues q_in, q_out that are used to pass questions and answers between browser and server
    +    """
    +
    +    steps = []
    +    title = "Zero Chat Bot"
    +    alert_view_url = None
    +
    +    def __init__(self, **kwargs):
    +        """
    +        Keyword Args
    +            any extra kwargs that is passed while creating the session
    +            (i.e. can be used for passing any query parameters)
    +        """
    +        self.session_id = str(uuid.uuid4())
    +        self.kwargs = kwargs
    +        self.spawn = kwargs.get("spawn", True)
    +        self._state = {}
    +        self._current_step = 0
    +        self._steps_info = {}
    +        self._last_output = None
    +        self._fetch_greenlet = None
    +        self._greenlet = None
    +        self._queue_out = gevent.queue.Queue()
    +        self._queue_in = gevent.queue.Queue()
    +        self._start()
    +
    +    @property
    +    def step_info(self):
    +        return self._steps_info.setdefault(self._current_step, {"slide": 0})
    +
    +    @property
    +    def is_first_slide(self):
    +        return self.step_info.get("slide", 1) == 1
    +
    +    @property
    +    def is_first_step(self):
    +        return self._current_step == 0
    +
    +    @property
    +    def is_last_step(self):
    +        return self._current_step >= len(self.steps) - 1
    +
    +    @property
    +    def info(self):
    +        previous = True
    +        if self.is_first_slide:
    +            if self.is_first_step or not self.step_info.get("previous"):
    +                previous = False
    +
    +        return {
    +            "step": self._current_step + 1,
    +            "steps": len(self.steps),
    +            "title": self.step_info.get("title"),
    +            "previous": previous,
    +            "last_step": self.is_last_step,
    +            "first_step": self.is_first_step,
    +            "first_slide": self.is_first_slide,
    +            "slide": self.step_info.get("slide", 1),
    +            "final_step": self.step_info.get("final_step"),
    +        }
    +
    +    def _execute_current_step(self, spawn=None):
    +        if spawn is None:
    +            spawn = self.spawn
    +
    +        def wrapper(step_name):
    +            internal_error = False
    +            try:
    +                getattr(self, step_name)()
    +            except StopChatFlow as e:
    +                internal_error = True
    +                j.logger.exception(f"chatflow stopped in step {step_name}. exception: {str(e)}", exception=e)
    +                traceback_info = j.tools.errorhandler.get_traceback()
    +                j.tools.alerthandler.alert_raise(
    +                    app_name="chatflows",
    +                    category="internal_errors",
    +                    message=str(e),
    +                    alert_type="exception",
    +                    traceback=traceback_info,
    +                )
    +                if e.msg:
    +                    self.send_error(
    +                        e.msg + f". Use the refresh button on the upper right to restart {self.title} creation",
    +                        **e.kwargs,
    +                    )
    +                self.send_data({"category": "end"})
    +
    +            except Exception as e:
    +                message = "Something wrong happened"
    +                if isinstance(e, stellar_sdk.exceptions.BadRequestError) and "op_underfunded" in e.extras.get(
    +                    "result_codes", {}
    +                ).get("operations", []):
    +                    message = "Not enough funds"
    +                internal_error = True
    +                j.logger.exception(f"error when executing step {step_name}. exception: {str(e)}", exception=e)
    +                traceback_info = j.tools.errorhandler.get_traceback()
    +                alert = j.tools.alerthandler.alert_raise(
    +                    app_name="chatflows",
    +                    category="internal_errors",
    +                    message=str(e),
    +                    alert_type="exception",
    +                    traceback=traceback_info,
    +                )
    +                username = self.user_info()["username"]
    +                if self.alert_view_url:
    +                    self.send_error(
    +                        f"""{message}, please check alert: <a href="{self.alert_view_url}/{alert.id}" target="_parent">{alert.id} </a>. This could occur if Stellar service was down."""
    +                        f"Use the refresh button on the upper right to restart {self.title} creation",
    +                        md=True,
    +                        html=True,
    +                    )
    +                elif username in j.core.identity.me.admins:
    +                    self.send_error(
    +                        f"""{message}, please check alert: <a href="/admin/#/alerts/{alert.id}" target="_parent">{alert.id} </a>. This could occur if Stellar service was down."""
    +                        f"Use the refresh button on the upper right to restart {self.title} creation",
    +                        md=True,
    +                        html=True,
    +                    )
    +                else:
    +                    self.send_error(
    +                        f"Something wrong happened, please contact support with alert ID: {alert.id}\n"
    +                        f"Use the refresh button on the upper right to restart {self.title} creation"
    +                    )
    +                self.send_data({"category": "end"})
    +
    +            if not internal_error:
    +                if self.is_last_step:
    +                    self.send_data({"category": "end"})
    +                else:
    +                    self._current_step += 1
    +                    self._execute_current_step(spawn=False)
    +
    +        step_name = self.steps[self._current_step]
    +        self.step_info["slide"] = 0
    +
    +        if spawn:
    +            self._greenlet = gevent.spawn(wrapper, step_name)
    +        else:
    +            wrapper(step_name)
    +
    +    def _start(self):
    +        self._execute_current_step()
    +
    +    def go_next(self):
    +        self._current_step += 1
    +        self._execute_current_step()
    +
    +    def go_back(self):
    +        if self.is_first_slide:
    +            if self.is_first_step:
    +                return
    +            else:
    +                self._current_step -= 1
    +
    +        self._greenlet.kill()
    +        return self._execute_current_step()
    +
    +    def get_work(self, restore=False):
    +        if self._fetch_greenlet:
    +            if not self._fetch_greenlet.ready():
    +                self._fetch_greenlet.kill()
    +
    +        if restore and self._last_output:
    +            return self._last_output
    +
    +        self._fetch_greenlet = gevent.spawn(self._queue_out.get)
    +        result = self._fetch_greenlet.get()
    +
    +        if not isinstance(result, gevent.GreenletExit):
    +            return result
    +
    +    def set_work(self, data):
    +        return self._queue_in.put(data)
    +
    +    def send_data(self, data, is_slide=False):
    +        data.setdefault("kwargs", {})
    +        retry = data["kwargs"].pop("retry", False)
    +
    +        if is_slide and not retry:
    +            self.step_info["slide"] += 1
    +
    +        output = {"info": self.info, "payload": data}
    +        self._last_output = output
    +        self._queue_out.put(output)
    +
    +    def send_error(self, message, **kwargs):
    +        self.send_data({"category": "error", "msg": message, "kwargs": kwargs})
    +        self._queue_in.get()
    +
    +    def ask(self, data):
    +        self.send_data(data, is_slide=True)
    +        return self._queue_in.get()
    +
    +    def user_info(self, **kwargs):
    +        self.send_data({"category": "user_info", "kwargs": kwargs})
    +        result = j.data.serializers.json.loads(self._queue_in.get())
    +        return result
    +
    +    def string_msg(self, msg, **kwargs):
    +        return {"category": "string_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def string_ask(self, msg, **kwargs):
    +        """Ask for a string value
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min_length (int): min length
    +            max_length (int): max length
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.string_msg(msg, **kwargs))
    +
    +    def secret_msg(self, msg, **kwargs):
    +        return {"category": "secret_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def secret_ask(self, msg, **kwargs):
    +        """Ask for a secret value
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min_length (int): min length
    +            max_length (int): max length
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.secret_msg(msg, **kwargs))
    +
    +    def int_msg(self, msg, **kwargs):
    +        return {"category": "int_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def int_ask(self, msg, **kwargs):
    +        """Ask for a inegert value
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min (int): min value
    +            max (int): max value
    +
    +        Returns:
    +            str: user input
    +        """
    +        result = self.ask(self.int_msg(msg, **kwargs))
    +        if result:
    +            return int(result)
    +
    +    def text_msg(self, msg, **kwargs):
    +        return {"category": "text_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def text_ask(self, msg, **kwargs):
    +        """Ask for a multi line string value
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.text_msg(msg, **kwargs))
    +
    +    def single_choice_msg(self, msg, options, **kwargs):
    +        return {"category": "single_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +    def single_choice(self, msg, options, **kwargs):
    +        """Ask for a single option
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.single_choice_msg(msg, options, **kwargs))
    +
    +    def multi_choice_msg(self, msg, options, **kwargs):
    +        return {"category": "multi_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +    def multi_choice(self, msg, options, **kwargs):
    +        """Ask for a multiple options
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min_options (int): min number of selected options
    +            max_options (int): max number selected options
    +
    +        Returns:
    +            str: user input
    +        """
    +        result = self.ask(self.multi_choice_msg(msg, options, **kwargs))
    +        return j.data.serializers.json.loads(result)
    +
    +    def multi_list_choice_msg(self, msg, options, **kwargs):
    +        return {"category": "multi_list_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +    def multi_list_choice(self, msg, options, **kwargs):
    +        """Ask for a multiple options
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min_options (int): min number of selected options
    +            max_options (int): max number selected options
    +
    +        Returns:
    +            str: user input
    +        """
    +        result = self.ask(self.multi_list_choice_msg(msg, options, **kwargs))
    +        return j.data.serializers.json.loads(result)
    +
    +    def drop_down_choice_msg(self, msg, options, **kwargs):
    +        return {"category": "drop_down_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +    def drop_down_choice(self, msg, options, **kwargs):
    +        """Ask for a single options using dropdown
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.drop_down_choice_msg(msg, options, **kwargs))
    +
    +    def autocomplete_drop_down(self, msg, options, **kwargs):
    +        """Ask for a single options using dropdown with auto completion
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.drop_down_choice(msg, options, auto_complete=True, **kwargs)
    +
    +    def datetime_picker_msg(self, msg, **kwargs):
    +        return {"category": "datetime_picker", "msg": msg, "kwargs": kwargs}
    +
    +    def datetime_picker(self, msg, **kwargs):
    +        """Ask for a datetime
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            int: timestamp
    +        """
    +        result = self.ask(self.datetime_picker_msg(msg, **kwargs))
    +        if result:
    +            return int(result)
    +
    +    def time_delta_msg(self, msg, **kwargs):
    +        return {"category": "time_delta", "msg": msg, "kwargs": kwargs}
    +
    +    def time_delta_ask(self, msg, **kwargs):
    +        """Ask for a time delta example: 1Y 1M 1w 2d 1h
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            datetime.datetime: user input
    +        """
    +        result = self.ask(self.time_delta_msg(msg, timedelta=True, **kwargs))
    +        return j.data.time.get(result).humanize()
    +
    +    def location_msg(self, msg, **kwargs):
    +        return {"category": "location_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def location_ask(self, msg, **kwargs):
    +        """Ask for a location [lng, lat]
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            list: list([lat, lng])
    +        """
    +        result = self.ask(self.location_msg(msg, **kwargs))
    +        return j.data.serializers.json.loads(result)
    +
    +    def download_file(self, msg, data, filename, **kwargs):
    +        """Add a download button to download data as a file
    +
    +        Args:
    +            msg (str): message text
    +            data (str): the data to be in the file
    +            filename (str): file name
    +
    +        Keyword Arguments:
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        """
    +        self.ask({"category": "download_file", "msg": msg, "data": data, "filename": filename, "kwargs": kwargs})
    +
    +    def upload_file_msg(self, msg, **kwargs):
    +        return {"category": "upload_file", "msg": msg, "kwargs": kwargs}
    +
    +    def upload_file(self, msg, **kwargs):
    +        """Ask for a file to be uploaded
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            max_size (int): file max size
    +            allowed_types: list of allowed types example : ['text/plain']
    +
    +        Returns:
    +            str: file content
    +        """
    +        return self.ask(self.upload_file_msg(msg, **kwargs))
    +
    +    def qrcode_show(self, msg, data, scale=10, **kwargs):
    +        """Show QR code as an image
    +
    +        Args:
    +            msg (str): message
    +            data (str): data to be encoded
    +            scale (int, optional): qrcode scale. Defaults to 10.
    +
    +        Keyword Arguments:
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +        """
    +        qrcode = j.tools.qrcode.base64_get(data, scale=scale)
    +        self.send_data({"category": "qrcode_show", "msg": msg, "qrcode": qrcode, "kwargs": kwargs}, is_slide=True)
    +        self._queue_in.get()
    +
    +    def md_msg(self, msg, **kwargs):
    +        return {"category": "md_show", "msg": msg, "kwargs": kwargs}
    +
    +    def md_show(self, msg, **kwargs):
    +        """Show markdown
    +
    +        Args:
    +            msg (str): markdown string
    +        """
    +        self.send_data(self.md_msg(msg, **kwargs), is_slide=True)
    +        self._queue_in.get()
    +
    +    def md_show_confirm(self, data, **kwargs):
    +        """Show a table contains the keys and values of the data dict
    +
    +        Args:
    +            data (dict): the data to be shown in the table
    +        """
    +        if "msg" in kwargs:
    +            msg = kwargs["msg"]
    +        else:
    +            msg = "Please make sure of the entered values before starting deployment"
    +
    +        self.send_data({"category": "confirm", "data": data, "kwargs": kwargs, "msg": msg}, is_slide=True)
    +        self._queue_in.get()
    +
    +    def loading_show(self, msg, wait, **kwargs):
    +        """Show a progress bar
    +
    +        Args:
    +            msg (str): message
    +            wait (int): the duration (in seconds) of the progress bar
    +
    +        Keyword Arguments:
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +        """
    +        data = {"category": "loading", "msg": msg, "kwargs": kwargs}
    +        for i in range(wait):
    +            data["value"] = (i / wait) * 100
    +            self.send_data(data)
    +            gevent.sleep(1)
    +
    +    def md_show_update(self, msg, **kwargs):
    +        self.send_data({"category": "infinite_loading", "msg": msg, "kwargs": kwargs}, is_slide=False)
    +
    +    def multi_values_ask(self, msg, **kwargs):
    +        """Ask for multiple values
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            dict: the result as a dict
    +        """
    +        result = self.ask({"category": "ask_multi_values", "msg": msg, "kwargs": kwargs})
    +        return j.data.serializers.json.loads(result)
    +
    +    def new_form(self):
    +        """Create a new form
    +
    +        Returns:
    +            Form: form object
    +        """
    +        return Form(self)
    +
    +    def stop(self, msg=None, **kwargs):
    +        raise StopChatFlow(msg=msg, **kwargs)
    +
    +    def end(self):
    +        self.send_data({"category": "end"})
    +
    +
    +def chatflow_step(title=None, final_step=False, disable_previous=False):
    +    def decorator(func):
    +        def wrapper(*args, **kwargs):
    +            self_ = args[0]
    +            self_.step_info.update(title=title, slide=0, previous=(not disable_previous), final_step=final_step)
    +            return func(*args, **kwargs)
    +
    +        return wrapper
    +
    +    return decorator
    +
    +
    +class StopChatFlow(Exception):
    +    def __init__(self, msg=None, **kwargs):
    +        super().__init__(self, msg)
    +        self.msg = msg
    +        self.kwargs = kwargs
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def chatflow_step(title=None, final_step=False, disable_previous=False) +
    +
    +
    +
    + +Expand source code + +
    def chatflow_step(title=None, final_step=False, disable_previous=False):
    +    def decorator(func):
    +        def wrapper(*args, **kwargs):
    +            self_ = args[0]
    +            self_.step_info.update(title=title, slide=0, previous=(not disable_previous), final_step=final_step)
    +            return func(*args, **kwargs)
    +
    +        return wrapper
    +
    +    return decorator
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Form +(session) +
    +
    +
    +
    + +Expand source code + +
    class Form:
    +    def __init__(self, session):
    +        self._session = session
    +        self.fields = []
    +        self.results = []
    +
    +    def ask(self, msg=None, **kwargs):
    +        self._session.send_data(
    +            {"category": "form", "msg": msg, "fields": self.fields, "kwargs": kwargs}, is_slide=True
    +        )
    +        results = j.data.serializers.json.loads(self._session._queue_in.get())
    +        for result, resobject in zip(results, self.results):
    +            resobject.value = result
    +
    +    def _append(self, msg, loader=str):
    +        self.fields.append(msg)
    +        result = Result(loader)
    +        self.results.append(result)
    +        return result
    +
    +    def string_ask(self, msg, **kwargs):
    +        return self._append(self._session.string_msg(msg, **kwargs))
    +
    +    def int_ask(self, msg, **kwargs):
    +        return self._append(self._session.int_msg(msg, **kwargs), int)
    +
    +    def secret_ask(self, msg, **kwargs):
    +        return self._append(self._session.secret_msg(msg, **kwargs))
    +
    +    def datetime_picker(self, msg, **kwargs):
    +        return self._append(self._session.datetime_picker_msg(msg, **kwargs))
    +
    +    def multi_list_choice(self, msg, options, **kwargs):
    +        return self._append(self._session.multi_list_choice_msg(msg, options, **kwargs))
    +
    +    def upload_file(self, msg, **kwargs):
    +        return self._append(self._session.upload_file_msg(msg, **kwargs))
    +
    +    def multi_choice(self, msg, options, **kwargs):
    +        return self._append(self._session.multi_choice_msg(msg, options, **kwargs), j.data.serializers.json.loads)
    +
    +    def single_choice(self, msg, options, **kwargs):
    +        return self._append(self._session.single_choice_msg(msg, options, **kwargs))
    +
    +    def drop_down_choice(self, msg, options, **kwargs):
    +        return self._append(self._session.drop_down_choice_msg(msg, options, **kwargs))
    +
    +

    Methods

    +
    +
    +def ask(self, msg=None, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def ask(self, msg=None, **kwargs):
    +    self._session.send_data(
    +        {"category": "form", "msg": msg, "fields": self.fields, "kwargs": kwargs}, is_slide=True
    +    )
    +    results = j.data.serializers.json.loads(self._session._queue_in.get())
    +    for result, resobject in zip(results, self.results):
    +        resobject.value = result
    +
    +
    +
    +def datetime_picker(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def datetime_picker(self, msg, **kwargs):
    +    return self._append(self._session.datetime_picker_msg(msg, **kwargs))
    +
    +
    +
    +def drop_down_choice(self, msg, options, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def drop_down_choice(self, msg, options, **kwargs):
    +    return self._append(self._session.drop_down_choice_msg(msg, options, **kwargs))
    +
    +
    +
    +def int_ask(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def int_ask(self, msg, **kwargs):
    +    return self._append(self._session.int_msg(msg, **kwargs), int)
    +
    +
    +
    +def multi_choice(self, msg, options, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def multi_choice(self, msg, options, **kwargs):
    +    return self._append(self._session.multi_choice_msg(msg, options, **kwargs), j.data.serializers.json.loads)
    +
    +
    +
    +def multi_list_choice(self, msg, options, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def multi_list_choice(self, msg, options, **kwargs):
    +    return self._append(self._session.multi_list_choice_msg(msg, options, **kwargs))
    +
    +
    +
    +def secret_ask(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def secret_ask(self, msg, **kwargs):
    +    return self._append(self._session.secret_msg(msg, **kwargs))
    +
    +
    +
    +def single_choice(self, msg, options, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def single_choice(self, msg, options, **kwargs):
    +    return self._append(self._session.single_choice_msg(msg, options, **kwargs))
    +
    +
    +
    +def string_ask(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def string_ask(self, msg, **kwargs):
    +    return self._append(self._session.string_msg(msg, **kwargs))
    +
    +
    +
    +def upload_file(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def upload_file(self, msg, **kwargs):
    +    return self._append(self._session.upload_file_msg(msg, **kwargs))
    +
    +
    +
    +
    +
    +class GedisChatBot +(**kwargs) +
    +
    +

    Contains the basic helper methods for asking questions +It also have the main queues q_in, q_out that are used to pass questions and answers between browser and server

    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class GedisChatBot:
    +    """
    +    Contains the basic helper methods for asking questions
    +    It also have the main queues q_in, q_out that are used to pass questions and answers between browser and server
    +    """
    +
    +    steps = []
    +    title = "Zero Chat Bot"
    +    alert_view_url = None
    +
    +    def __init__(self, **kwargs):
    +        """
    +        Keyword Args
    +            any extra kwargs that is passed while creating the session
    +            (i.e. can be used for passing any query parameters)
    +        """
    +        self.session_id = str(uuid.uuid4())
    +        self.kwargs = kwargs
    +        self.spawn = kwargs.get("spawn", True)
    +        self._state = {}
    +        self._current_step = 0
    +        self._steps_info = {}
    +        self._last_output = None
    +        self._fetch_greenlet = None
    +        self._greenlet = None
    +        self._queue_out = gevent.queue.Queue()
    +        self._queue_in = gevent.queue.Queue()
    +        self._start()
    +
    +    @property
    +    def step_info(self):
    +        return self._steps_info.setdefault(self._current_step, {"slide": 0})
    +
    +    @property
    +    def is_first_slide(self):
    +        return self.step_info.get("slide", 1) == 1
    +
    +    @property
    +    def is_first_step(self):
    +        return self._current_step == 0
    +
    +    @property
    +    def is_last_step(self):
    +        return self._current_step >= len(self.steps) - 1
    +
    +    @property
    +    def info(self):
    +        previous = True
    +        if self.is_first_slide:
    +            if self.is_first_step or not self.step_info.get("previous"):
    +                previous = False
    +
    +        return {
    +            "step": self._current_step + 1,
    +            "steps": len(self.steps),
    +            "title": self.step_info.get("title"),
    +            "previous": previous,
    +            "last_step": self.is_last_step,
    +            "first_step": self.is_first_step,
    +            "first_slide": self.is_first_slide,
    +            "slide": self.step_info.get("slide", 1),
    +            "final_step": self.step_info.get("final_step"),
    +        }
    +
    +    def _execute_current_step(self, spawn=None):
    +        if spawn is None:
    +            spawn = self.spawn
    +
    +        def wrapper(step_name):
    +            internal_error = False
    +            try:
    +                getattr(self, step_name)()
    +            except StopChatFlow as e:
    +                internal_error = True
    +                j.logger.exception(f"chatflow stopped in step {step_name}. exception: {str(e)}", exception=e)
    +                traceback_info = j.tools.errorhandler.get_traceback()
    +                j.tools.alerthandler.alert_raise(
    +                    app_name="chatflows",
    +                    category="internal_errors",
    +                    message=str(e),
    +                    alert_type="exception",
    +                    traceback=traceback_info,
    +                )
    +                if e.msg:
    +                    self.send_error(
    +                        e.msg + f". Use the refresh button on the upper right to restart {self.title} creation",
    +                        **e.kwargs,
    +                    )
    +                self.send_data({"category": "end"})
    +
    +            except Exception as e:
    +                message = "Something wrong happened"
    +                if isinstance(e, stellar_sdk.exceptions.BadRequestError) and "op_underfunded" in e.extras.get(
    +                    "result_codes", {}
    +                ).get("operations", []):
    +                    message = "Not enough funds"
    +                internal_error = True
    +                j.logger.exception(f"error when executing step {step_name}. exception: {str(e)}", exception=e)
    +                traceback_info = j.tools.errorhandler.get_traceback()
    +                alert = j.tools.alerthandler.alert_raise(
    +                    app_name="chatflows",
    +                    category="internal_errors",
    +                    message=str(e),
    +                    alert_type="exception",
    +                    traceback=traceback_info,
    +                )
    +                username = self.user_info()["username"]
    +                if self.alert_view_url:
    +                    self.send_error(
    +                        f"""{message}, please check alert: <a href="{self.alert_view_url}/{alert.id}" target="_parent">{alert.id} </a>. This could occur if Stellar service was down."""
    +                        f"Use the refresh button on the upper right to restart {self.title} creation",
    +                        md=True,
    +                        html=True,
    +                    )
    +                elif username in j.core.identity.me.admins:
    +                    self.send_error(
    +                        f"""{message}, please check alert: <a href="/admin/#/alerts/{alert.id}" target="_parent">{alert.id} </a>. This could occur if Stellar service was down."""
    +                        f"Use the refresh button on the upper right to restart {self.title} creation",
    +                        md=True,
    +                        html=True,
    +                    )
    +                else:
    +                    self.send_error(
    +                        f"Something wrong happened, please contact support with alert ID: {alert.id}\n"
    +                        f"Use the refresh button on the upper right to restart {self.title} creation"
    +                    )
    +                self.send_data({"category": "end"})
    +
    +            if not internal_error:
    +                if self.is_last_step:
    +                    self.send_data({"category": "end"})
    +                else:
    +                    self._current_step += 1
    +                    self._execute_current_step(spawn=False)
    +
    +        step_name = self.steps[self._current_step]
    +        self.step_info["slide"] = 0
    +
    +        if spawn:
    +            self._greenlet = gevent.spawn(wrapper, step_name)
    +        else:
    +            wrapper(step_name)
    +
    +    def _start(self):
    +        self._execute_current_step()
    +
    +    def go_next(self):
    +        self._current_step += 1
    +        self._execute_current_step()
    +
    +    def go_back(self):
    +        if self.is_first_slide:
    +            if self.is_first_step:
    +                return
    +            else:
    +                self._current_step -= 1
    +
    +        self._greenlet.kill()
    +        return self._execute_current_step()
    +
    +    def get_work(self, restore=False):
    +        if self._fetch_greenlet:
    +            if not self._fetch_greenlet.ready():
    +                self._fetch_greenlet.kill()
    +
    +        if restore and self._last_output:
    +            return self._last_output
    +
    +        self._fetch_greenlet = gevent.spawn(self._queue_out.get)
    +        result = self._fetch_greenlet.get()
    +
    +        if not isinstance(result, gevent.GreenletExit):
    +            return result
    +
    +    def set_work(self, data):
    +        return self._queue_in.put(data)
    +
    +    def send_data(self, data, is_slide=False):
    +        data.setdefault("kwargs", {})
    +        retry = data["kwargs"].pop("retry", False)
    +
    +        if is_slide and not retry:
    +            self.step_info["slide"] += 1
    +
    +        output = {"info": self.info, "payload": data}
    +        self._last_output = output
    +        self._queue_out.put(output)
    +
    +    def send_error(self, message, **kwargs):
    +        self.send_data({"category": "error", "msg": message, "kwargs": kwargs})
    +        self._queue_in.get()
    +
    +    def ask(self, data):
    +        self.send_data(data, is_slide=True)
    +        return self._queue_in.get()
    +
    +    def user_info(self, **kwargs):
    +        self.send_data({"category": "user_info", "kwargs": kwargs})
    +        result = j.data.serializers.json.loads(self._queue_in.get())
    +        return result
    +
    +    def string_msg(self, msg, **kwargs):
    +        return {"category": "string_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def string_ask(self, msg, **kwargs):
    +        """Ask for a string value
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min_length (int): min length
    +            max_length (int): max length
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.string_msg(msg, **kwargs))
    +
    +    def secret_msg(self, msg, **kwargs):
    +        return {"category": "secret_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def secret_ask(self, msg, **kwargs):
    +        """Ask for a secret value
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min_length (int): min length
    +            max_length (int): max length
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.secret_msg(msg, **kwargs))
    +
    +    def int_msg(self, msg, **kwargs):
    +        return {"category": "int_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def int_ask(self, msg, **kwargs):
    +        """Ask for a inegert value
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min (int): min value
    +            max (int): max value
    +
    +        Returns:
    +            str: user input
    +        """
    +        result = self.ask(self.int_msg(msg, **kwargs))
    +        if result:
    +            return int(result)
    +
    +    def text_msg(self, msg, **kwargs):
    +        return {"category": "text_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def text_ask(self, msg, **kwargs):
    +        """Ask for a multi line string value
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.text_msg(msg, **kwargs))
    +
    +    def single_choice_msg(self, msg, options, **kwargs):
    +        return {"category": "single_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +    def single_choice(self, msg, options, **kwargs):
    +        """Ask for a single option
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.single_choice_msg(msg, options, **kwargs))
    +
    +    def multi_choice_msg(self, msg, options, **kwargs):
    +        return {"category": "multi_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +    def multi_choice(self, msg, options, **kwargs):
    +        """Ask for a multiple options
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min_options (int): min number of selected options
    +            max_options (int): max number selected options
    +
    +        Returns:
    +            str: user input
    +        """
    +        result = self.ask(self.multi_choice_msg(msg, options, **kwargs))
    +        return j.data.serializers.json.loads(result)
    +
    +    def multi_list_choice_msg(self, msg, options, **kwargs):
    +        return {"category": "multi_list_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +    def multi_list_choice(self, msg, options, **kwargs):
    +        """Ask for a multiple options
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            min_options (int): min number of selected options
    +            max_options (int): max number selected options
    +
    +        Returns:
    +            str: user input
    +        """
    +        result = self.ask(self.multi_list_choice_msg(msg, options, **kwargs))
    +        return j.data.serializers.json.loads(result)
    +
    +    def drop_down_choice_msg(self, msg, options, **kwargs):
    +        return {"category": "drop_down_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +    def drop_down_choice(self, msg, options, **kwargs):
    +        """Ask for a single options using dropdown
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.ask(self.drop_down_choice_msg(msg, options, **kwargs))
    +
    +    def autocomplete_drop_down(self, msg, options, **kwargs):
    +        """Ask for a single options using dropdown with auto completion
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            str: user input
    +        """
    +        return self.drop_down_choice(msg, options, auto_complete=True, **kwargs)
    +
    +    def datetime_picker_msg(self, msg, **kwargs):
    +        return {"category": "datetime_picker", "msg": msg, "kwargs": kwargs}
    +
    +    def datetime_picker(self, msg, **kwargs):
    +        """Ask for a datetime
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            int: timestamp
    +        """
    +        result = self.ask(self.datetime_picker_msg(msg, **kwargs))
    +        if result:
    +            return int(result)
    +
    +    def time_delta_msg(self, msg, **kwargs):
    +        return {"category": "time_delta", "msg": msg, "kwargs": kwargs}
    +
    +    def time_delta_ask(self, msg, **kwargs):
    +        """Ask for a time delta example: 1Y 1M 1w 2d 1h
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            datetime.datetime: user input
    +        """
    +        result = self.ask(self.time_delta_msg(msg, timedelta=True, **kwargs))
    +        return j.data.time.get(result).humanize()
    +
    +    def location_msg(self, msg, **kwargs):
    +        return {"category": "location_ask", "msg": msg, "kwargs": kwargs}
    +
    +    def location_ask(self, msg, **kwargs):
    +        """Ask for a location [lng, lat]
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            list: list([lat, lng])
    +        """
    +        result = self.ask(self.location_msg(msg, **kwargs))
    +        return j.data.serializers.json.loads(result)
    +
    +    def download_file(self, msg, data, filename, **kwargs):
    +        """Add a download button to download data as a file
    +
    +        Args:
    +            msg (str): message text
    +            data (str): the data to be in the file
    +            filename (str): file name
    +
    +        Keyword Arguments:
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        """
    +        self.ask({"category": "download_file", "msg": msg, "data": data, "filename": filename, "kwargs": kwargs})
    +
    +    def upload_file_msg(self, msg, **kwargs):
    +        return {"category": "upload_file", "msg": msg, "kwargs": kwargs}
    +
    +    def upload_file(self, msg, **kwargs):
    +        """Ask for a file to be uploaded
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +            max_size (int): file max size
    +            allowed_types: list of allowed types example : ['text/plain']
    +
    +        Returns:
    +            str: file content
    +        """
    +        return self.ask(self.upload_file_msg(msg, **kwargs))
    +
    +    def qrcode_show(self, msg, data, scale=10, **kwargs):
    +        """Show QR code as an image
    +
    +        Args:
    +            msg (str): message
    +            data (str): data to be encoded
    +            scale (int, optional): qrcode scale. Defaults to 10.
    +
    +        Keyword Arguments:
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +        """
    +        qrcode = j.tools.qrcode.base64_get(data, scale=scale)
    +        self.send_data({"category": "qrcode_show", "msg": msg, "qrcode": qrcode, "kwargs": kwargs}, is_slide=True)
    +        self._queue_in.get()
    +
    +    def md_msg(self, msg, **kwargs):
    +        return {"category": "md_show", "msg": msg, "kwargs": kwargs}
    +
    +    def md_show(self, msg, **kwargs):
    +        """Show markdown
    +
    +        Args:
    +            msg (str): markdown string
    +        """
    +        self.send_data(self.md_msg(msg, **kwargs), is_slide=True)
    +        self._queue_in.get()
    +
    +    def md_show_confirm(self, data, **kwargs):
    +        """Show a table contains the keys and values of the data dict
    +
    +        Args:
    +            data (dict): the data to be shown in the table
    +        """
    +        if "msg" in kwargs:
    +            msg = kwargs["msg"]
    +        else:
    +            msg = "Please make sure of the entered values before starting deployment"
    +
    +        self.send_data({"category": "confirm", "data": data, "kwargs": kwargs, "msg": msg}, is_slide=True)
    +        self._queue_in.get()
    +
    +    def loading_show(self, msg, wait, **kwargs):
    +        """Show a progress bar
    +
    +        Args:
    +            msg (str): message
    +            wait (int): the duration (in seconds) of the progress bar
    +
    +        Keyword Arguments:
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +        """
    +        data = {"category": "loading", "msg": msg, "kwargs": kwargs}
    +        for i in range(wait):
    +            data["value"] = (i / wait) * 100
    +            self.send_data(data)
    +            gevent.sleep(1)
    +
    +    def md_show_update(self, msg, **kwargs):
    +        self.send_data({"category": "infinite_loading", "msg": msg, "kwargs": kwargs}, is_slide=False)
    +
    +    def multi_values_ask(self, msg, **kwargs):
    +        """Ask for multiple values
    +
    +        Args:
    +            msg (str): message text
    +
    +        Keyword Arguments:
    +            required (bool): flag to make this field required
    +            md (bool): render message as markdown
    +            html (bool): render message as html
    +
    +        Returns:
    +            dict: the result as a dict
    +        """
    +        result = self.ask({"category": "ask_multi_values", "msg": msg, "kwargs": kwargs})
    +        return j.data.serializers.json.loads(result)
    +
    +    def new_form(self):
    +        """Create a new form
    +
    +        Returns:
    +            Form: form object
    +        """
    +        return Form(self)
    +
    +    def stop(self, msg=None, **kwargs):
    +        raise StopChatFlow(msg=msg, **kwargs)
    +
    +    def end(self):
    +        self.send_data({"category": "end"})
    +
    +

    Subclasses

    + +

    Class variables

    +
    +
    var alert_view_url
    +
    +
    +
    +
    var steps
    +
    +
    +
    +
    var title
    +
    +
    +
    +
    +

    Instance variables

    +
    +
    var info
    +
    +
    +
    + +Expand source code + +
    @property
    +def info(self):
    +    previous = True
    +    if self.is_first_slide:
    +        if self.is_first_step or not self.step_info.get("previous"):
    +            previous = False
    +
    +    return {
    +        "step": self._current_step + 1,
    +        "steps": len(self.steps),
    +        "title": self.step_info.get("title"),
    +        "previous": previous,
    +        "last_step": self.is_last_step,
    +        "first_step": self.is_first_step,
    +        "first_slide": self.is_first_slide,
    +        "slide": self.step_info.get("slide", 1),
    +        "final_step": self.step_info.get("final_step"),
    +    }
    +
    +
    +
    var is_first_slide
    +
    +
    +
    + +Expand source code + +
    @property
    +def is_first_slide(self):
    +    return self.step_info.get("slide", 1) == 1
    +
    +
    +
    var is_first_step
    +
    +
    +
    + +Expand source code + +
    @property
    +def is_first_step(self):
    +    return self._current_step == 0
    +
    +
    +
    var is_last_step
    +
    +
    +
    + +Expand source code + +
    @property
    +def is_last_step(self):
    +    return self._current_step >= len(self.steps) - 1
    +
    +
    +
    var step_info
    +
    +
    +
    + +Expand source code + +
    @property
    +def step_info(self):
    +    return self._steps_info.setdefault(self._current_step, {"slide": 0})
    +
    +
    +
    +

    Methods

    +
    +
    +def ask(self, data) +
    +
    +
    +
    + +Expand source code + +
    def ask(self, data):
    +    self.send_data(data, is_slide=True)
    +    return self._queue_in.get()
    +
    +
    +
    +def autocomplete_drop_down(self, msg, options, **kwargs) +
    +
    +

    Ask for a single options using dropdown with auto completion

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def autocomplete_drop_down(self, msg, options, **kwargs):
    +    """Ask for a single options using dropdown with auto completion
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    Returns:
    +        str: user input
    +    """
    +    return self.drop_down_choice(msg, options, auto_complete=True, **kwargs)
    +
    +
    +
    +def datetime_picker(self, msg, **kwargs) +
    +
    +

    Ask for a datetime

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html

    +

    Returns

    +
    +
    int
    +
    timestamp
    +
    +
    + +Expand source code + +
    def datetime_picker(self, msg, **kwargs):
    +    """Ask for a datetime
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    Returns:
    +        int: timestamp
    +    """
    +    result = self.ask(self.datetime_picker_msg(msg, **kwargs))
    +    if result:
    +        return int(result)
    +
    +
    +
    +def datetime_picker_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def datetime_picker_msg(self, msg, **kwargs):
    +    return {"category": "datetime_picker", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def download_file(self, msg, data, filename, **kwargs) +
    +
    +

    Add a download button to download data as a file

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    data : str
    +
    the data to be in the file
    +
    filename : str
    +
    file name
    +
    +

    Keyword Arguments: +md (bool): render message as markdown +html (bool): render message as html

    +
    + +Expand source code + +
    def download_file(self, msg, data, filename, **kwargs):
    +    """Add a download button to download data as a file
    +
    +    Args:
    +        msg (str): message text
    +        data (str): the data to be in the file
    +        filename (str): file name
    +
    +    Keyword Arguments:
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    """
    +    self.ask({"category": "download_file", "msg": msg, "data": data, "filename": filename, "kwargs": kwargs})
    +
    +
    +
    +def drop_down_choice(self, msg, options, **kwargs) +
    +
    +

    Ask for a single options using dropdown

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def drop_down_choice(self, msg, options, **kwargs):
    +    """Ask for a single options using dropdown
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    Returns:
    +        str: user input
    +    """
    +    return self.ask(self.drop_down_choice_msg(msg, options, **kwargs))
    +
    +
    +
    +def drop_down_choice_msg(self, msg, options, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def drop_down_choice_msg(self, msg, options, **kwargs):
    +    return {"category": "drop_down_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +
    +
    +def end(self) +
    +
    +
    +
    + +Expand source code + +
    def end(self):
    +    self.send_data({"category": "end"})
    +
    +
    +
    +def get_work(self, restore=False) +
    +
    +
    +
    + +Expand source code + +
    def get_work(self, restore=False):
    +    if self._fetch_greenlet:
    +        if not self._fetch_greenlet.ready():
    +            self._fetch_greenlet.kill()
    +
    +    if restore and self._last_output:
    +        return self._last_output
    +
    +    self._fetch_greenlet = gevent.spawn(self._queue_out.get)
    +    result = self._fetch_greenlet.get()
    +
    +    if not isinstance(result, gevent.GreenletExit):
    +        return result
    +
    +
    +
    +def go_back(self) +
    +
    +
    +
    + +Expand source code + +
    def go_back(self):
    +    if self.is_first_slide:
    +        if self.is_first_step:
    +            return
    +        else:
    +            self._current_step -= 1
    +
    +    self._greenlet.kill()
    +    return self._execute_current_step()
    +
    +
    +
    +def go_next(self) +
    +
    +
    +
    + +Expand source code + +
    def go_next(self):
    +    self._current_step += 1
    +    self._execute_current_step()
    +
    +
    +
    +def int_ask(self, msg, **kwargs) +
    +
    +

    Ask for a inegert value

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html +min (int): min value +max (int): max value

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def int_ask(self, msg, **kwargs):
    +    """Ask for a inegert value
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +        min (int): min value
    +        max (int): max value
    +
    +    Returns:
    +        str: user input
    +    """
    +    result = self.ask(self.int_msg(msg, **kwargs))
    +    if result:
    +        return int(result)
    +
    +
    +
    +def int_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def int_msg(self, msg, **kwargs):
    +    return {"category": "int_ask", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def loading_show(self, msg, wait, **kwargs) +
    +
    +

    Show a progress bar

    +

    Args

    +
    +
    msg : str
    +
    message
    +
    wait : int
    +
    the duration (in seconds) of the progress bar
    +
    +

    Keyword Arguments: +md (bool): render message as markdown +html (bool): render message as html

    +
    + +Expand source code + +
    def loading_show(self, msg, wait, **kwargs):
    +    """Show a progress bar
    +
    +    Args:
    +        msg (str): message
    +        wait (int): the duration (in seconds) of the progress bar
    +
    +    Keyword Arguments:
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +    """
    +    data = {"category": "loading", "msg": msg, "kwargs": kwargs}
    +    for i in range(wait):
    +        data["value"] = (i / wait) * 100
    +        self.send_data(data)
    +        gevent.sleep(1)
    +
    +
    +
    +def location_ask(self, msg, **kwargs) +
    +
    +

    Ask for a location [lng, lat]

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html

    +

    Returns

    +
    +
    list
    +
    list([lat, lng])
    +
    +
    + +Expand source code + +
    def location_ask(self, msg, **kwargs):
    +    """Ask for a location [lng, lat]
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    Returns:
    +        list: list([lat, lng])
    +    """
    +    result = self.ask(self.location_msg(msg, **kwargs))
    +    return j.data.serializers.json.loads(result)
    +
    +
    +
    +def location_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def location_msg(self, msg, **kwargs):
    +    return {"category": "location_ask", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def md_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def md_msg(self, msg, **kwargs):
    +    return {"category": "md_show", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def md_show(self, msg, **kwargs) +
    +
    +

    Show markdown

    +

    Args

    +
    +
    msg : str
    +
    markdown string
    +
    +
    + +Expand source code + +
    def md_show(self, msg, **kwargs):
    +    """Show markdown
    +
    +    Args:
    +        msg (str): markdown string
    +    """
    +    self.send_data(self.md_msg(msg, **kwargs), is_slide=True)
    +    self._queue_in.get()
    +
    +
    +
    +def md_show_confirm(self, data, **kwargs) +
    +
    +

    Show a table contains the keys and values of the data dict

    +

    Args

    +
    +
    data : dict
    +
    the data to be shown in the table
    +
    +
    + +Expand source code + +
    def md_show_confirm(self, data, **kwargs):
    +    """Show a table contains the keys and values of the data dict
    +
    +    Args:
    +        data (dict): the data to be shown in the table
    +    """
    +    if "msg" in kwargs:
    +        msg = kwargs["msg"]
    +    else:
    +        msg = "Please make sure of the entered values before starting deployment"
    +
    +    self.send_data({"category": "confirm", "data": data, "kwargs": kwargs, "msg": msg}, is_slide=True)
    +    self._queue_in.get()
    +
    +
    +
    +def md_show_update(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def md_show_update(self, msg, **kwargs):
    +    self.send_data({"category": "infinite_loading", "msg": msg, "kwargs": kwargs}, is_slide=False)
    +
    +
    +
    +def multi_choice(self, msg, options, **kwargs) +
    +
    +

    Ask for a multiple options

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html +min_options (int): min number of selected options +max_options (int): max number selected options

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def multi_choice(self, msg, options, **kwargs):
    +    """Ask for a multiple options
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +        min_options (int): min number of selected options
    +        max_options (int): max number selected options
    +
    +    Returns:
    +        str: user input
    +    """
    +    result = self.ask(self.multi_choice_msg(msg, options, **kwargs))
    +    return j.data.serializers.json.loads(result)
    +
    +
    +
    +def multi_choice_msg(self, msg, options, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def multi_choice_msg(self, msg, options, **kwargs):
    +    return {"category": "multi_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +
    +
    +def multi_list_choice(self, msg, options, **kwargs) +
    +
    +

    Ask for a multiple options

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html +min_options (int): min number of selected options +max_options (int): max number selected options

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def multi_list_choice(self, msg, options, **kwargs):
    +    """Ask for a multiple options
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +        min_options (int): min number of selected options
    +        max_options (int): max number selected options
    +
    +    Returns:
    +        str: user input
    +    """
    +    result = self.ask(self.multi_list_choice_msg(msg, options, **kwargs))
    +    return j.data.serializers.json.loads(result)
    +
    +
    +
    +def multi_list_choice_msg(self, msg, options, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def multi_list_choice_msg(self, msg, options, **kwargs):
    +    return {"category": "multi_list_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +
    +
    +def multi_values_ask(self, msg, **kwargs) +
    +
    +

    Ask for multiple values

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html

    +

    Returns

    +
    +
    dict
    +
    the result as a dict
    +
    +
    + +Expand source code + +
    def multi_values_ask(self, msg, **kwargs):
    +    """Ask for multiple values
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    Returns:
    +        dict: the result as a dict
    +    """
    +    result = self.ask({"category": "ask_multi_values", "msg": msg, "kwargs": kwargs})
    +    return j.data.serializers.json.loads(result)
    +
    +
    +
    +def new_form(self) +
    +
    +

    Create a new form

    +

    Returns

    +
    +
    Form
    +
    form object
    +
    +
    + +Expand source code + +
    def new_form(self):
    +    """Create a new form
    +
    +    Returns:
    +        Form: form object
    +    """
    +    return Form(self)
    +
    +
    +
    +def qrcode_show(self, msg, data, scale=10, **kwargs) +
    +
    +

    Show QR code as an image

    +

    Args

    +
    +
    msg : str
    +
    message
    +
    data : str
    +
    data to be encoded
    +
    scale : int, optional
    +
    qrcode scale. Defaults to 10.
    +
    +

    Keyword Arguments: +md (bool): render message as markdown +html (bool): render message as html

    +
    + +Expand source code + +
    def qrcode_show(self, msg, data, scale=10, **kwargs):
    +    """Show QR code as an image
    +
    +    Args:
    +        msg (str): message
    +        data (str): data to be encoded
    +        scale (int, optional): qrcode scale. Defaults to 10.
    +
    +    Keyword Arguments:
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +    """
    +    qrcode = j.tools.qrcode.base64_get(data, scale=scale)
    +    self.send_data({"category": "qrcode_show", "msg": msg, "qrcode": qrcode, "kwargs": kwargs}, is_slide=True)
    +    self._queue_in.get()
    +
    +
    +
    +def secret_ask(self, msg, **kwargs) +
    +
    +

    Ask for a secret value

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html +min_length (int): min length +max_length (int): max length

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def secret_ask(self, msg, **kwargs):
    +    """Ask for a secret value
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +        min_length (int): min length
    +        max_length (int): max length
    +
    +    Returns:
    +        str: user input
    +    """
    +    return self.ask(self.secret_msg(msg, **kwargs))
    +
    +
    +
    +def secret_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def secret_msg(self, msg, **kwargs):
    +    return {"category": "secret_ask", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def send_data(self, data, is_slide=False) +
    +
    +
    +
    + +Expand source code + +
    def send_data(self, data, is_slide=False):
    +    data.setdefault("kwargs", {})
    +    retry = data["kwargs"].pop("retry", False)
    +
    +    if is_slide and not retry:
    +        self.step_info["slide"] += 1
    +
    +    output = {"info": self.info, "payload": data}
    +    self._last_output = output
    +    self._queue_out.put(output)
    +
    +
    +
    +def send_error(self, message, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def send_error(self, message, **kwargs):
    +    self.send_data({"category": "error", "msg": message, "kwargs": kwargs})
    +    self._queue_in.get()
    +
    +
    +
    +def set_work(self, data) +
    +
    +
    +
    + +Expand source code + +
    def set_work(self, data):
    +    return self._queue_in.put(data)
    +
    +
    +
    +def single_choice(self, msg, options, **kwargs) +
    +
    +

    Ask for a single option

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def single_choice(self, msg, options, **kwargs):
    +    """Ask for a single option
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    Returns:
    +        str: user input
    +    """
    +    return self.ask(self.single_choice_msg(msg, options, **kwargs))
    +
    +
    +
    +def single_choice_msg(self, msg, options, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def single_choice_msg(self, msg, options, **kwargs):
    +    return {"category": "single_choice", "msg": msg, "options": options, "kwargs": kwargs}
    +
    +
    +
    +def stop(self, msg=None, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def stop(self, msg=None, **kwargs):
    +    raise StopChatFlow(msg=msg, **kwargs)
    +
    +
    +
    +def string_ask(self, msg, **kwargs) +
    +
    +

    Ask for a string value

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html +min_length (int): min length +max_length (int): max length

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def string_ask(self, msg, **kwargs):
    +    """Ask for a string value
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +        min_length (int): min length
    +        max_length (int): max length
    +
    +    Returns:
    +        str: user input
    +    """
    +    return self.ask(self.string_msg(msg, **kwargs))
    +
    +
    +
    +def string_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def string_msg(self, msg, **kwargs):
    +    return {"category": "string_ask", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def text_ask(self, msg, **kwargs) +
    +
    +

    Ask for a multi line string value

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html

    +

    Returns

    +
    +
    str
    +
    user input
    +
    +
    + +Expand source code + +
    def text_ask(self, msg, **kwargs):
    +    """Ask for a multi line string value
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    Returns:
    +        str: user input
    +    """
    +    return self.ask(self.text_msg(msg, **kwargs))
    +
    +
    +
    +def text_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def text_msg(self, msg, **kwargs):
    +    return {"category": "text_ask", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def time_delta_ask(self, msg, **kwargs) +
    +
    +

    Ask for a time delta example: 1Y 1M 1w 2d 1h

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html

    +

    Returns

    +
    +
    datetime.datetime
    +
    user input
    +
    +
    + +Expand source code + +
    def time_delta_ask(self, msg, **kwargs):
    +    """Ask for a time delta example: 1Y 1M 1w 2d 1h
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +
    +    Returns:
    +        datetime.datetime: user input
    +    """
    +    result = self.ask(self.time_delta_msg(msg, timedelta=True, **kwargs))
    +    return j.data.time.get(result).humanize()
    +
    +
    +
    +def time_delta_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def time_delta_msg(self, msg, **kwargs):
    +    return {"category": "time_delta", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def upload_file(self, msg, **kwargs) +
    +
    +

    Ask for a file to be uploaded

    +

    Args

    +
    +
    msg : str
    +
    message text
    +
    +

    Keyword Arguments: +required (bool): flag to make this field required +md (bool): render message as markdown +html (bool): render message as html +max_size (int): file max size +allowed_types: list of allowed types example : ['text/plain']

    +

    Returns

    +
    +
    str
    +
    file content
    +
    +
    + +Expand source code + +
    def upload_file(self, msg, **kwargs):
    +    """Ask for a file to be uploaded
    +
    +    Args:
    +        msg (str): message text
    +
    +    Keyword Arguments:
    +        required (bool): flag to make this field required
    +        md (bool): render message as markdown
    +        html (bool): render message as html
    +        max_size (int): file max size
    +        allowed_types: list of allowed types example : ['text/plain']
    +
    +    Returns:
    +        str: file content
    +    """
    +    return self.ask(self.upload_file_msg(msg, **kwargs))
    +
    +
    +
    +def upload_file_msg(self, msg, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def upload_file_msg(self, msg, **kwargs):
    +    return {"category": "upload_file", "msg": msg, "kwargs": kwargs}
    +
    +
    +
    +def user_info(self, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def user_info(self, **kwargs):
    +    self.send_data({"category": "user_info", "kwargs": kwargs})
    +    result = j.data.serializers.json.loads(self._queue_in.get())
    +    return result
    +
    +
    +
    +
    +
    +class Result +(loader=builtins.str) +
    +
    +
    +
    + +Expand source code + +
    class Result:
    +    def __init__(self, loader=str):
    +        self._value = None
    +        self._loader = loader
    +
    +    @property
    +    def value(self):
    +        return self._value
    +
    +    @value.setter
    +    def value(self, value):
    +        self._value = self._loader(value)
    +
    +

    Instance variables

    +
    +
    var value
    +
    +
    +
    + +Expand source code + +
    @property
    +def value(self):
    +    return self._value
    +
    +
    +
    +
    +
    +class StopChatFlow +(msg=None, **kwargs) +
    +
    +

    Common base class for all non-exit exceptions.

    +
    + +Expand source code + +
    class StopChatFlow(Exception):
    +    def __init__(self, msg=None, **kwargs):
    +        super().__init__(self, msg)
    +        self.msg = msg
    +        self.kwargs = kwargs
    +
    +

    Ancestors

    +
      +
    • builtins.Exception
    • +
    • builtins.BaseException
    • +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/chatflows/index.html b/docs/api/jumpscale/sals/chatflows/index.html new file mode 100644 index 000000000..33e726da2 --- /dev/null +++ b/docs/api/jumpscale/sals/chatflows/index.html @@ -0,0 +1,107 @@ + + + + + + +jumpscale.sals.chatflows API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/chatflows/models/index.html b/docs/api/jumpscale/sals/chatflows/models/index.html new file mode 100644 index 000000000..d5a01be84 --- /dev/null +++ b/docs/api/jumpscale/sals/chatflows/models/index.html @@ -0,0 +1,65 @@ + + + + + + +jumpscale.sals.chatflows.models API documentation + + + + + + + + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/chatflows/models/voter_model.html b/docs/api/jumpscale/sals/chatflows/models/voter_model.html new file mode 100644 index 000000000..01e3f021f --- /dev/null +++ b/docs/api/jumpscale/sals/chatflows/models/voter_model.html @@ -0,0 +1,424 @@ + + + + + + +jumpscale.sals.chatflows.models.voter_model API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.chatflows.models.voter_model

    +
    +
    +
    + +Expand source code + +
    from jumpscale.core.base import Base, fields
    +
    +
    +class User(Base):
    +    user_code = fields.String(default="")
    +    poll_name = fields.String(default="")
    +    wallets_addresses = fields.List(fields.String())
    +    transaction_hashes = fields.List(fields.String())
    +    tokens = fields.Float(default=0.0)
    +    vote_data = fields.Typed(dict, default={})
    +    extra_data = fields.Typed(dict, default={})
    +    vote_data_weighted = fields.Typed(dict, default={})
    +    has_voted = fields.Boolean(default=False)
    +    manifesto_version = fields.String(default="2.0.0")
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class User +(parent_=None, instance_name_=None, **values) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class User(Base):
    +    user_code = fields.String(default="")
    +    poll_name = fields.String(default="")
    +    wallets_addresses = fields.List(fields.String())
    +    transaction_hashes = fields.List(fields.String())
    +    tokens = fields.Float(default=0.0)
    +    vote_data = fields.Typed(dict, default={})
    +    extra_data = fields.Typed(dict, default={})
    +    vote_data_weighted = fields.Typed(dict, default={})
    +    has_voted = fields.Boolean(default=False)
    +    manifesto_version = fields.String(default="2.0.0")
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var extra_data
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var has_voted
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var manifesto_version
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var poll_name
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var tokens
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var transaction_hashes
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var user_code
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var vote_data
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var vote_data_weighted
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var wallets_addresses
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/chatflows/polls.html b/docs/api/jumpscale/sals/chatflows/polls.html new file mode 100644 index 000000000..b65ef6375 --- /dev/null +++ b/docs/api/jumpscale/sals/chatflows/polls.html @@ -0,0 +1,901 @@ + + + + + + +jumpscale.sals.chatflows.polls API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.chatflows.polls

    +
    +
    +
    + +Expand source code + +
    from textwrap import dedent
    +
    +from jumpscale.core.base import StoredFactory
    +from jumpscale.loader import j
    +from jumpscale.sals.chatflows.chatflows import GedisChatBot, chatflow_step
    +from jumpscale.sals.chatflows.models.voter_model import User
    +
    +WALLET_NAME = "polls_receive"
    +MANIFESTO_VERSION = "2.0.1"
    +
    +all_users = StoredFactory(User)
    +all_users.always_reload = True
    +
    +
    +class Poll(GedisChatBot):
    +    """Polls chatflow base
    +    just inherit from this class and override poll_name and QUESTIONS in your chatflow
    +
    +    Args:
    +        GedisChatBot (Parent): contains the chatflows sals main functions
    +
    +    Raises:
    +        j.core.exceptions.Runtime: if wrong inheritance happens
    +        StopChatFlow: if payment is failed
    +    """
    +
    +    poll_name = None  # Required
    +
    +    steps = ["initialize", "welcome", "payment", "custom_votes", "result"]
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.QUESTIONS = {}
    +        self.extra_data = {}
    +        self.metadata = {}
    +        self.custom_answers = {}
    +
    +        if not j.clients.stellar.find(WALLET_NAME):
    +            raise j.core.exceptions.Runtime(f"Wallet {WALLET_NAME} is not configured, please create it.")
    +
    +        self.wallet = j.clients.stellar.get(WALLET_NAME)
    +
    +    def _get_wallets_as_md(self, wallets):
    +        result = "\n"
    +        for item in wallets:
    +            result += f"- `{item}` has {self._get_voter_balance(item)} (TFT+TFTA)\n"
    +        return result
    +
    +    @chatflow_step()
    +    def initialize(self):
    +        user_info = self.user_info()
    +
    +        username = user_info["username"].split(".")[0]
    +        welcome_message = f"# Welcome `{username}` to {self.poll_name.capitalize()} Poll\n<br/>The detailed poll results are only visible to the tfgrid council members"
    +        self.user = all_users.get(name=f"{self.poll_name}_{username}")
    +        self.user.poll_name = self.poll_name
    +        if self.user.has_voted:
    +            welcome_message += "\n<br/><br/>`Note: You have already voted.`"
    +
    +        if self.user.has_voted:
    +            actions = ["Edit My Vote", "See Results"]
    +            action = self.single_choice(welcome_message, options=actions, required=True, md=True)
    +            if action == actions[1]:
    +                self.result()
    +                self.end()
    +        else:
    +            self.md_show(welcome_message, md=True)
    +
    +    @chatflow_step()
    +    def welcome(self):
    +        pass
    +
    +    @chatflow_step(title="Loading Wallets")
    +    def payment(self):
    +        def _pay(msg=""):
    +            amount = 0.1
    +            currency = self.single_choice(
    +                "We need to know how many tokens you have to allow weighted vote results, "
    +                "in order to do this we need to know all of your wallets addresses you want us to consider in this poll. "
    +                "The idea is you send us a small transaction that costs 0.1 tokens. "
    +                "Then we will be able to calculate the sum of the TFTs and TFTAs you have in all of the wallets you added. Now you can start adding your wallets "
    +                "Which token would you like to continue the transaction with?",
    +                ["TFT", "TFTA"],
    +                required=True,
    +            )
    +
    +            qr_code_content = j.sals.zos.get()._escrow_to_qrcode(
    +                escrow_address=self.wallet.address,
    +                escrow_asset=currency,
    +                total_amount=amount,
    +                message=self.user.user_code,
    +            )
    +
    +            message_text = f"""\
    +            <h3>Make a Payment</h3>
    +            Scan the QR code with your wallet (do not change the message) or enter the information below manually and proceed with the payment.
    +            Make sure to add the message (user code) as memo_text
    +            Please make the transaction and press Next
    +            <h4> Wallet address: </h4>  {self.wallet.address}
    +            <h4> Currency: </h4>  {currency}
    +            <h4> Amount: </h4>  {amount}
    +            <h4> Message (User code): </h4>  {self.user.user_code}
    +            """
    +            self.qrcode_show(data=qr_code_content, msg=dedent(message_text), scale=4, update=True, html=True, md=True)
    +            if self._check_payment(timeout=360):
    +                return True
    +            else:
    +                return False
    +
    +        def _pay_again(msg=""):
    +            while True:
    +                pay_again = self.single_choice(
    +                    msg
    +                    or f"Wallets added: {self._get_wallets_as_md(self.user.wallets_addresses)}\nDo you like to add another wallet?",
    +                    ["YES", "NO"],
    +                    md=True,
    +                )
    +                if pay_again == "NO":
    +                    break
    +                if not _pay():
    +                    _pay_again(
    +                        "Error adding the wallet, Please make sure you transaction is completed.\n do you want to try again ?"
    +                    )
    +
    +        if not self.user.user_code:
    +            self.user.user_code = j.data.idgenerator.chars(10)
    +        # Payment
    +        if self.user.has_voted and len(self.user.wallets_addresses) > 0:
    +            self.md_show(
    +                f"You have already added wallets: {self._get_wallets_as_md(self.user.wallets_addresses)}\n, Press Next to add another wallet and modify your vote",
    +                md=True,
    +            )
    +            _pay_again()
    +
    +        elif len(self.user.wallets_addresses) > 0:
    +            self.md_show(
    +                f"You have already added wallets: {self._get_wallets_as_md(self.user.wallets_addresses)}\n, Press Next to add another wallet and submit your vote",
    +                md=True,
    +            )
    +            _pay_again()
    +        else:
    +            if _pay():
    +                _pay_again()
    +            else:
    +                self.stop("Error adding the wallet, Please make sure you transaction is completed.\n Please try again")
    +
    +    def _check_payment(self, timeout):
    +        """Returns True if user has paid already, False if not"""
    +        now = j.data.time.get().timestamp
    +        remaning_time = j.data.time.get(now + timeout).timestamp
    +        while remaning_time > now:
    +            remaning_time_msg = j.data.time.get(remaning_time).humanize(granularity=["minute", "second"])
    +            payment_message = (
    +                "# Payment being processed...\n"
    +                f"Process will be cancelled if payment is not successful {remaning_time_msg}"
    +            )
    +            self.md_show_update(payment_message, md=True)
    +            user_wallets_count = len(self.user.wallets_addresses)
    +            transactions = self.wallet.list_transactions()
    +            for transaction in transactions:
    +                if transaction.memo_text == self.user.user_code:
    +                    if transaction.hash not in self.user.transaction_hashes:
    +                        self.user.transaction_hashes.append(transaction.hash)
    +                    user_wallet = self.wallet.get_sender_wallet_address(transaction.hash)
    +                    if not user_wallet in self.user.wallets_addresses:
    +                        self.user.wallets_addresses.append(user_wallet)
    +                        self.user.tokens += float(self._get_voter_balance(user_wallet))
    +                    self.user.save()
    +            if len(self.user.wallets_addresses) > user_wallets_count:
    +                return True
    +        return False
    +
    +    def get_vote_answer(self, vote_title):
    +        answer_array = self.user.vote_data.get(vote_title)
    +        if answer_array:
    +            options = self.QUESTIONS.get(vote_title)
    +            try:
    +                return options[answer_array.index(1)]
    +            except ValueError:
    +                pass
    +
    +    def get_question_answer(self, question_title):
    +        return self.user.extra_data.get(question_title)
    +
    +    def vote(self):
    +        answers = {}
    +        answers.update(self.custom_answers)
    +        vote_data = self._map_vote_results(answers.copy())
    +        vote_data_weighted = self._map_vote_results(answers.copy(), weighted=True)
    +        self.user.vote_data = vote_data
    +        self.user.vote_data_weighted = vote_data_weighted
    +        self.user.has_voted = True
    +        self.user.extra_data = self.extra_data
    +        self.user.manifesto_version = MANIFESTO_VERSION
    +        self.user.save()
    +
    +    @chatflow_step(title="Please fill in the following form", disable_previous=True)
    +    def custom_votes(self):
    +        """allow child classes to have its custom slides
    +
    +        Returns:
    +            Dict, Dict: Has all questions and answer, extra saved data outside the poll
    +        """
    +        pass
    +
    +    def _map_vote_results(self, form_answers, weighted=False):
    +        """takes form answers and returns a sparse array of what user chose
    +        to be easy in calcualting votes
    +
    +        example: ["Blue", "Red", "Green", "Orange"]
    +        if user chose "Red" will [0, 1, 0, 0]
    +        if user chose "Red" and weighted results will [0, <user_token_sum>, 0, 0]
    +        Args:
    +            form_answers (dict): form result dictionary
    +        """
    +        for question, answer in form_answers.items():
    +            all_answers_init = len(self.QUESTIONS[question]) * [0.0]
    +            answer_index = self.QUESTIONS[question].index(answer)
    +            if weighted:
    +                all_answers_init[answer_index] = self.user.tokens
    +            else:
    +                all_answers_init[answer_index] = 1
    +            form_answers[question] = all_answers_init
    +        return form_answers
    +
    +    @chatflow_step(title="Poll Results %", final_step=True)
    +    def result(self):
    +        usersnames = all_users.list_all()
    +        total_votes = 0
    +        total_answers = {}
    +        total_answers_weighted = {}
    +        for username in usersnames:
    +            user = all_users.get(username)
    +            if user.poll_name == self.poll_name and user.has_voted:
    +                total_votes += 1
    +                user_votes = all_users.get(username).vote_data
    +                user_votes_weighted = all_users.get(username).vote_data_weighted
    +                for question, answer in user_votes.items():
    +                    if total_answers.get(question):
    +                        total_answers[question] = list(map(sum, zip(total_answers[question], answer)))
    +                    else:
    +                        total_answers[question] = answer
    +
    +                for question, answer in user_votes_weighted.items():
    +                    if total_answers_weighted.get(question):
    +                        total_answers_weighted[question] = list(map(sum, zip(total_answers_weighted[question], answer)))
    +                    else:
    +                        total_answers_weighted[question] = answer
    +
    +        total_answers_with_percent = {k: self._calculate_percent(v) for k, v in total_answers.items()}
    +        total_answers_weighted_with_percent = {k: self._calculate_percent(v) for k, v in total_answers_weighted.items()}
    +
    +        result_msg = ""
    +        for question, answers in total_answers_with_percent.items():
    +            question_current_title = question
    +            question_new_title = self.metadata["new_title_keys"][question_current_title]
    +            result_msg += f"### {question_new_title}\n"
    +            for i in range(len(answers)):
    +                answer_name = self.QUESTIONS[question][i]
    +                result_msg += f"- {answer_name}: {answers[i]}%\n"
    +            result_msg += "\n\n"
    +
    +        # result_msg += "\n<br />\n\n"
    +        # result_msg += "## Weighted results %\n\n<br />\n\n"
    +        # for question, answers in total_answers_weighted_with_percent.items():
    +        #     question_current_title = question
    +        #     question_new_title = self.metadata["new_title_keys"][question_current_title]
    +        #     result_msg += f"### {question_new_title}\n"
    +        #     for i in range(len(answers)):
    +        #         answer_name = self.QUESTIONS[question][i]
    +        #         result_msg += f"- {answer_name}: {answers[i]}%\n"
    +        #     result_msg += "\n"
    +
    +        result_msg += f"\n<br />\n\n#### Total number of votes: {total_votes}\n"
    +        self.md_show(result_msg, md=True)
    +
    +    def _calculate_percent(self, answers):
    +        """Takes the answers list which is a sparse array and map it
    +        to percentages
    +
    +        Args:
    +            answers (list)
    +
    +        Returns:
    +            list: answers_list mapped to percentages
    +        """
    +        answers_list = answers[:]
    +        total_votes = float(sum(answers_list))
    +        for i in range(len(answers_list)):
    +            res = (answers_list[i] / total_votes) * 100
    +            answers_list[i] = round(res, 2)
    +        return answers_list
    +
    +    def _get_voter_balance(self, wallet_address):
    +        """Get sum of user TFT and TFTA
    +
    +        Args:
    +            wallet_address (String): Wallet address
    +        """
    +        assets = self.wallet.get_balance(wallet_address)
    +        total_balance = 0.0
    +        # get free balances
    +        for asset in assets.balances:
    +            if asset.asset_code == "TFT" or asset.asset_code == "TFTA":
    +                total_balance += float(asset.balance)
    +
    +        # add locked funds too
    +        for locked_account in assets.escrow_accounts:
    +            for locked_asset in locked_account.balances:
    +                if locked_asset.asset_code == "TFT" or locked_asset.asset_code == "TFTA":
    +                    total_balance += float(locked_asset.balance)
    +
    +        return total_balance
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Poll +(*args, **kwargs) +
    +
    +

    Polls chatflow base +just inherit from this class and override poll_name and QUESTIONS in your chatflow

    +

    Args

    +
    +
    GedisChatBot : Parent
    +
    contains the chatflows sals main functions
    +
    +

    Raises

    +
    +
    j.core.exceptions.Runtime
    +
    if wrong inheritance happens
    +
    StopChatFlow
    +
    if payment is failed
    +
    +

    Keyword Args +any extra kwargs that is passed while creating the session +(i.e. can be used for passing any query parameters)

    +
    + +Expand source code + +
    class Poll(GedisChatBot):
    +    """Polls chatflow base
    +    just inherit from this class and override poll_name and QUESTIONS in your chatflow
    +
    +    Args:
    +        GedisChatBot (Parent): contains the chatflows sals main functions
    +
    +    Raises:
    +        j.core.exceptions.Runtime: if wrong inheritance happens
    +        StopChatFlow: if payment is failed
    +    """
    +
    +    poll_name = None  # Required
    +
    +    steps = ["initialize", "welcome", "payment", "custom_votes", "result"]
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.QUESTIONS = {}
    +        self.extra_data = {}
    +        self.metadata = {}
    +        self.custom_answers = {}
    +
    +        if not j.clients.stellar.find(WALLET_NAME):
    +            raise j.core.exceptions.Runtime(f"Wallet {WALLET_NAME} is not configured, please create it.")
    +
    +        self.wallet = j.clients.stellar.get(WALLET_NAME)
    +
    +    def _get_wallets_as_md(self, wallets):
    +        result = "\n"
    +        for item in wallets:
    +            result += f"- `{item}` has {self._get_voter_balance(item)} (TFT+TFTA)\n"
    +        return result
    +
    +    @chatflow_step()
    +    def initialize(self):
    +        user_info = self.user_info()
    +
    +        username = user_info["username"].split(".")[0]
    +        welcome_message = f"# Welcome `{username}` to {self.poll_name.capitalize()} Poll\n<br/>The detailed poll results are only visible to the tfgrid council members"
    +        self.user = all_users.get(name=f"{self.poll_name}_{username}")
    +        self.user.poll_name = self.poll_name
    +        if self.user.has_voted:
    +            welcome_message += "\n<br/><br/>`Note: You have already voted.`"
    +
    +        if self.user.has_voted:
    +            actions = ["Edit My Vote", "See Results"]
    +            action = self.single_choice(welcome_message, options=actions, required=True, md=True)
    +            if action == actions[1]:
    +                self.result()
    +                self.end()
    +        else:
    +            self.md_show(welcome_message, md=True)
    +
    +    @chatflow_step()
    +    def welcome(self):
    +        pass
    +
    +    @chatflow_step(title="Loading Wallets")
    +    def payment(self):
    +        def _pay(msg=""):
    +            amount = 0.1
    +            currency = self.single_choice(
    +                "We need to know how many tokens you have to allow weighted vote results, "
    +                "in order to do this we need to know all of your wallets addresses you want us to consider in this poll. "
    +                "The idea is you send us a small transaction that costs 0.1 tokens. "
    +                "Then we will be able to calculate the sum of the TFTs and TFTAs you have in all of the wallets you added. Now you can start adding your wallets "
    +                "Which token would you like to continue the transaction with?",
    +                ["TFT", "TFTA"],
    +                required=True,
    +            )
    +
    +            qr_code_content = j.sals.zos.get()._escrow_to_qrcode(
    +                escrow_address=self.wallet.address,
    +                escrow_asset=currency,
    +                total_amount=amount,
    +                message=self.user.user_code,
    +            )
    +
    +            message_text = f"""\
    +            <h3>Make a Payment</h3>
    +            Scan the QR code with your wallet (do not change the message) or enter the information below manually and proceed with the payment.
    +            Make sure to add the message (user code) as memo_text
    +            Please make the transaction and press Next
    +            <h4> Wallet address: </h4>  {self.wallet.address}
    +            <h4> Currency: </h4>  {currency}
    +            <h4> Amount: </h4>  {amount}
    +            <h4> Message (User code): </h4>  {self.user.user_code}
    +            """
    +            self.qrcode_show(data=qr_code_content, msg=dedent(message_text), scale=4, update=True, html=True, md=True)
    +            if self._check_payment(timeout=360):
    +                return True
    +            else:
    +                return False
    +
    +        def _pay_again(msg=""):
    +            while True:
    +                pay_again = self.single_choice(
    +                    msg
    +                    or f"Wallets added: {self._get_wallets_as_md(self.user.wallets_addresses)}\nDo you like to add another wallet?",
    +                    ["YES", "NO"],
    +                    md=True,
    +                )
    +                if pay_again == "NO":
    +                    break
    +                if not _pay():
    +                    _pay_again(
    +                        "Error adding the wallet, Please make sure you transaction is completed.\n do you want to try again ?"
    +                    )
    +
    +        if not self.user.user_code:
    +            self.user.user_code = j.data.idgenerator.chars(10)
    +        # Payment
    +        if self.user.has_voted and len(self.user.wallets_addresses) > 0:
    +            self.md_show(
    +                f"You have already added wallets: {self._get_wallets_as_md(self.user.wallets_addresses)}\n, Press Next to add another wallet and modify your vote",
    +                md=True,
    +            )
    +            _pay_again()
    +
    +        elif len(self.user.wallets_addresses) > 0:
    +            self.md_show(
    +                f"You have already added wallets: {self._get_wallets_as_md(self.user.wallets_addresses)}\n, Press Next to add another wallet and submit your vote",
    +                md=True,
    +            )
    +            _pay_again()
    +        else:
    +            if _pay():
    +                _pay_again()
    +            else:
    +                self.stop("Error adding the wallet, Please make sure you transaction is completed.\n Please try again")
    +
    +    def _check_payment(self, timeout):
    +        """Returns True if user has paid already, False if not"""
    +        now = j.data.time.get().timestamp
    +        remaning_time = j.data.time.get(now + timeout).timestamp
    +        while remaning_time > now:
    +            remaning_time_msg = j.data.time.get(remaning_time).humanize(granularity=["minute", "second"])
    +            payment_message = (
    +                "# Payment being processed...\n"
    +                f"Process will be cancelled if payment is not successful {remaning_time_msg}"
    +            )
    +            self.md_show_update(payment_message, md=True)
    +            user_wallets_count = len(self.user.wallets_addresses)
    +            transactions = self.wallet.list_transactions()
    +            for transaction in transactions:
    +                if transaction.memo_text == self.user.user_code:
    +                    if transaction.hash not in self.user.transaction_hashes:
    +                        self.user.transaction_hashes.append(transaction.hash)
    +                    user_wallet = self.wallet.get_sender_wallet_address(transaction.hash)
    +                    if not user_wallet in self.user.wallets_addresses:
    +                        self.user.wallets_addresses.append(user_wallet)
    +                        self.user.tokens += float(self._get_voter_balance(user_wallet))
    +                    self.user.save()
    +            if len(self.user.wallets_addresses) > user_wallets_count:
    +                return True
    +        return False
    +
    +    def get_vote_answer(self, vote_title):
    +        answer_array = self.user.vote_data.get(vote_title)
    +        if answer_array:
    +            options = self.QUESTIONS.get(vote_title)
    +            try:
    +                return options[answer_array.index(1)]
    +            except ValueError:
    +                pass
    +
    +    def get_question_answer(self, question_title):
    +        return self.user.extra_data.get(question_title)
    +
    +    def vote(self):
    +        answers = {}
    +        answers.update(self.custom_answers)
    +        vote_data = self._map_vote_results(answers.copy())
    +        vote_data_weighted = self._map_vote_results(answers.copy(), weighted=True)
    +        self.user.vote_data = vote_data
    +        self.user.vote_data_weighted = vote_data_weighted
    +        self.user.has_voted = True
    +        self.user.extra_data = self.extra_data
    +        self.user.manifesto_version = MANIFESTO_VERSION
    +        self.user.save()
    +
    +    @chatflow_step(title="Please fill in the following form", disable_previous=True)
    +    def custom_votes(self):
    +        """allow child classes to have its custom slides
    +
    +        Returns:
    +            Dict, Dict: Has all questions and answer, extra saved data outside the poll
    +        """
    +        pass
    +
    +    def _map_vote_results(self, form_answers, weighted=False):
    +        """takes form answers and returns a sparse array of what user chose
    +        to be easy in calcualting votes
    +
    +        example: ["Blue", "Red", "Green", "Orange"]
    +        if user chose "Red" will [0, 1, 0, 0]
    +        if user chose "Red" and weighted results will [0, <user_token_sum>, 0, 0]
    +        Args:
    +            form_answers (dict): form result dictionary
    +        """
    +        for question, answer in form_answers.items():
    +            all_answers_init = len(self.QUESTIONS[question]) * [0.0]
    +            answer_index = self.QUESTIONS[question].index(answer)
    +            if weighted:
    +                all_answers_init[answer_index] = self.user.tokens
    +            else:
    +                all_answers_init[answer_index] = 1
    +            form_answers[question] = all_answers_init
    +        return form_answers
    +
    +    @chatflow_step(title="Poll Results %", final_step=True)
    +    def result(self):
    +        usersnames = all_users.list_all()
    +        total_votes = 0
    +        total_answers = {}
    +        total_answers_weighted = {}
    +        for username in usersnames:
    +            user = all_users.get(username)
    +            if user.poll_name == self.poll_name and user.has_voted:
    +                total_votes += 1
    +                user_votes = all_users.get(username).vote_data
    +                user_votes_weighted = all_users.get(username).vote_data_weighted
    +                for question, answer in user_votes.items():
    +                    if total_answers.get(question):
    +                        total_answers[question] = list(map(sum, zip(total_answers[question], answer)))
    +                    else:
    +                        total_answers[question] = answer
    +
    +                for question, answer in user_votes_weighted.items():
    +                    if total_answers_weighted.get(question):
    +                        total_answers_weighted[question] = list(map(sum, zip(total_answers_weighted[question], answer)))
    +                    else:
    +                        total_answers_weighted[question] = answer
    +
    +        total_answers_with_percent = {k: self._calculate_percent(v) for k, v in total_answers.items()}
    +        total_answers_weighted_with_percent = {k: self._calculate_percent(v) for k, v in total_answers_weighted.items()}
    +
    +        result_msg = ""
    +        for question, answers in total_answers_with_percent.items():
    +            question_current_title = question
    +            question_new_title = self.metadata["new_title_keys"][question_current_title]
    +            result_msg += f"### {question_new_title}\n"
    +            for i in range(len(answers)):
    +                answer_name = self.QUESTIONS[question][i]
    +                result_msg += f"- {answer_name}: {answers[i]}%\n"
    +            result_msg += "\n\n"
    +
    +        # result_msg += "\n<br />\n\n"
    +        # result_msg += "## Weighted results %\n\n<br />\n\n"
    +        # for question, answers in total_answers_weighted_with_percent.items():
    +        #     question_current_title = question
    +        #     question_new_title = self.metadata["new_title_keys"][question_current_title]
    +        #     result_msg += f"### {question_new_title}\n"
    +        #     for i in range(len(answers)):
    +        #         answer_name = self.QUESTIONS[question][i]
    +        #         result_msg += f"- {answer_name}: {answers[i]}%\n"
    +        #     result_msg += "\n"
    +
    +        result_msg += f"\n<br />\n\n#### Total number of votes: {total_votes}\n"
    +        self.md_show(result_msg, md=True)
    +
    +    def _calculate_percent(self, answers):
    +        """Takes the answers list which is a sparse array and map it
    +        to percentages
    +
    +        Args:
    +            answers (list)
    +
    +        Returns:
    +            list: answers_list mapped to percentages
    +        """
    +        answers_list = answers[:]
    +        total_votes = float(sum(answers_list))
    +        for i in range(len(answers_list)):
    +            res = (answers_list[i] / total_votes) * 100
    +            answers_list[i] = round(res, 2)
    +        return answers_list
    +
    +    def _get_voter_balance(self, wallet_address):
    +        """Get sum of user TFT and TFTA
    +
    +        Args:
    +            wallet_address (String): Wallet address
    +        """
    +        assets = self.wallet.get_balance(wallet_address)
    +        total_balance = 0.0
    +        # get free balances
    +        for asset in assets.balances:
    +            if asset.asset_code == "TFT" or asset.asset_code == "TFTA":
    +                total_balance += float(asset.balance)
    +
    +        # add locked funds too
    +        for locked_account in assets.escrow_accounts:
    +            for locked_asset in locked_account.balances:
    +                if locked_asset.asset_code == "TFT" or locked_asset.asset_code == "TFTA":
    +                    total_balance += float(locked_asset.balance)
    +
    +        return total_balance
    +
    +

    Ancestors

    + +

    Subclasses

    + +

    Class variables

    +
    +
    var poll_name
    +
    +
    +
    +
    var steps
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def custom_votes(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def wrapper(*args, **kwargs):
    +    self_ = args[0]
    +    self_.step_info.update(title=title, slide=0, previous=(not disable_previous), final_step=final_step)
    +    return func(*args, **kwargs)
    +
    +
    +
    +def get_question_answer(self, question_title) +
    +
    +
    +
    + +Expand source code + +
    def get_question_answer(self, question_title):
    +    return self.user.extra_data.get(question_title)
    +
    +
    +
    +def get_vote_answer(self, vote_title) +
    +
    +
    +
    + +Expand source code + +
    def get_vote_answer(self, vote_title):
    +    answer_array = self.user.vote_data.get(vote_title)
    +    if answer_array:
    +        options = self.QUESTIONS.get(vote_title)
    +        try:
    +            return options[answer_array.index(1)]
    +        except ValueError:
    +            pass
    +
    +
    +
    +def initialize(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def wrapper(*args, **kwargs):
    +    self_ = args[0]
    +    self_.step_info.update(title=title, slide=0, previous=(not disable_previous), final_step=final_step)
    +    return func(*args, **kwargs)
    +
    +
    +
    +def payment(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def wrapper(*args, **kwargs):
    +    self_ = args[0]
    +    self_.step_info.update(title=title, slide=0, previous=(not disable_previous), final_step=final_step)
    +    return func(*args, **kwargs)
    +
    +
    +
    +def result(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def wrapper(*args, **kwargs):
    +    self_ = args[0]
    +    self_.step_info.update(title=title, slide=0, previous=(not disable_previous), final_step=final_step)
    +    return func(*args, **kwargs)
    +
    +
    +
    +def vote(self) +
    +
    +
    +
    + +Expand source code + +
    def vote(self):
    +    answers = {}
    +    answers.update(self.custom_answers)
    +    vote_data = self._map_vote_results(answers.copy())
    +    vote_data_weighted = self._map_vote_results(answers.copy(), weighted=True)
    +    self.user.vote_data = vote_data
    +    self.user.vote_data_weighted = vote_data_weighted
    +    self.user.has_voted = True
    +    self.user.extra_data = self.extra_data
    +    self.user.manifesto_version = MANIFESTO_VERSION
    +    self.user.save()
    +
    +
    +
    +def welcome(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def wrapper(*args, **kwargs):
    +    self_ = args[0]
    +    self_.step_info.update(title=title, slide=0, previous=(not disable_previous), final_step=final_step)
    +    return func(*args, **kwargs)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/crtsh/index.html b/docs/api/jumpscale/sals/crtsh/index.html new file mode 100644 index 000000000..8eaf642e6 --- /dev/null +++ b/docs/api/jumpscale/sals/crtsh/index.html @@ -0,0 +1,335 @@ + + + + + + +jumpscale.sals.crtsh API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.sals.crtsh

    +
    +
    +
    + +Expand source code + +
    import requests
    +from jumpscale.data import time as jstime
    +
    +
    +BASE_URL = "https://crt.sh/?q={}&output=json"
    +RATE_LIMIT = 50
    +
    +
    +def fetch_domain_certs(domain):
    +    """return all certificates issued to a specific domain
    +
    +    Args:
    +        domain (str): parent domain
    +
    +    Returns:
    +        list: of dicts of the certs. keys (issuer_ca_id, issuer_name, name_value, id, entry_timestamp, not_before, not_after)
    +
    +    Raises:
    +        requests.exceptions.HTTPError
    +    """
    +    url = BASE_URL.format(domain)
    +    result = requests.get(url)
    +    if result.status_code != 200:
    +        result.raise_for_status()
    +    return result.json()
    +
    +
    +def count_domain_certs_since(domain, days=7):
    +    """check if a domain has reached the rate limit for issues certs
    +
    +    Args:
    +        domain (str): parent domain
    +        days (int): number of days to be checked since
    +
    +    Returns:
    +        int: number of certs issued by letsencrypt
    +
    +    Raises:
    +        requests.exceptions.HTTPError
    +    """
    +    all_certs = fetch_domain_certs(domain)
    +    count = 0
    +    now = jstime.utcnow()
    +    domains = set()
    +    start_date = now.shift(days=-1 * days)
    +    for cert in all_certs:
    +        # rate limit is 50 certs every week. so we check how many certs were issued within the last 7 days
    +        # we will check using date only. entry_timestamp example "2020-08-23T12:15:27.833"
    +        # check only for letsencrypt
    +        if "Let's Encrypt" not in cert["issuer_name"]:
    +            continue
    +        t = jstime.Arrow.strptime(cert["entry_timestamp"].split("T")[0], "%Y-%m-%d").to("utc")
    +        subdomain = cert["name_value"].split(".")[0]
    +        if t >= start_date:
    +            domains.add(subdomain)
    +    count = len(domains)
    +    return count
    +
    +
    +def has_reached_limit(domain, limit=RATE_LIMIT):
    +    """check if a domain has reached the rate limit for issues certs
    +
    +    Args:
    +        domain (str): parent domain
    +        limit (int): limit to be checked against. defaults to 50
    +
    +    Returns:
    +        bool: True if the limit has been reached
    +
    +    Raises:
    +        requests.exceptions.HTTPError
    +    """
    +    count = count_domain_certs_since(domain)
    +    return count >= limit
    +
    +
    +def has_certificate(domain):
    +    """check if the specified domain name has an issued cert
    +
    +    Args:
    +        domain (str): parent domain
    +
    +    Returns:
    +        dict: cert dict if a cert was issued. else None
    +
    +    Raises:
    +        requests.exceptions.HTTPError
    +    """
    +    all_certs = fetch_domain_certs(domain)
    +    for cert in all_certs:
    +        if cert["name_value"] == domain:
    +            return cert
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def count_domain_certs_since(domain, days=7) +
    +
    +

    check if a domain has reached the rate limit for issues certs

    +

    Args

    +
    +
    domain : str
    +
    parent domain
    +
    days : int
    +
    number of days to be checked since
    +
    +

    Returns

    +
    +
    int
    +
    number of certs issued by letsencrypt
    +
    +

    Raises

    +

    requests.exceptions.HTTPError

    +
    + +Expand source code + +
    def count_domain_certs_since(domain, days=7):
    +    """check if a domain has reached the rate limit for issues certs
    +
    +    Args:
    +        domain (str): parent domain
    +        days (int): number of days to be checked since
    +
    +    Returns:
    +        int: number of certs issued by letsencrypt
    +
    +    Raises:
    +        requests.exceptions.HTTPError
    +    """
    +    all_certs = fetch_domain_certs(domain)
    +    count = 0
    +    now = jstime.utcnow()
    +    domains = set()
    +    start_date = now.shift(days=-1 * days)
    +    for cert in all_certs:
    +        # rate limit is 50 certs every week. so we check how many certs were issued within the last 7 days
    +        # we will check using date only. entry_timestamp example "2020-08-23T12:15:27.833"
    +        # check only for letsencrypt
    +        if "Let's Encrypt" not in cert["issuer_name"]:
    +            continue
    +        t = jstime.Arrow.strptime(cert["entry_timestamp"].split("T")[0], "%Y-%m-%d").to("utc")
    +        subdomain = cert["name_value"].split(".")[0]
    +        if t >= start_date:
    +            domains.add(subdomain)
    +    count = len(domains)
    +    return count
    +
    +
    +
    +def fetch_domain_certs(domain) +
    +
    +

    return all certificates issued to a specific domain

    +

    Args

    +
    +
    domain : str
    +
    parent domain
    +
    +

    Returns

    +
    +
    list
    +
    of dicts of the certs. keys (issuer_ca_id, issuer_name, name_value, id, entry_timestamp, not_before, not_after)
    +
    +

    Raises

    +

    requests.exceptions.HTTPError

    +
    + +Expand source code + +
    def fetch_domain_certs(domain):
    +    """return all certificates issued to a specific domain
    +
    +    Args:
    +        domain (str): parent domain
    +
    +    Returns:
    +        list: of dicts of the certs. keys (issuer_ca_id, issuer_name, name_value, id, entry_timestamp, not_before, not_after)
    +
    +    Raises:
    +        requests.exceptions.HTTPError
    +    """
    +    url = BASE_URL.format(domain)
    +    result = requests.get(url)
    +    if result.status_code != 200:
    +        result.raise_for_status()
    +    return result.json()
    +
    +
    +
    +def has_certificate(domain) +
    +
    +

    check if the specified domain name has an issued cert

    +

    Args

    +
    +
    domain : str
    +
    parent domain
    +
    +

    Returns

    +
    +
    dict
    +
    cert dict if a cert was issued. else None
    +
    +

    Raises

    +

    requests.exceptions.HTTPError

    +
    + +Expand source code + +
    def has_certificate(domain):
    +    """check if the specified domain name has an issued cert
    +
    +    Args:
    +        domain (str): parent domain
    +
    +    Returns:
    +        dict: cert dict if a cert was issued. else None
    +
    +    Raises:
    +        requests.exceptions.HTTPError
    +    """
    +    all_certs = fetch_domain_certs(domain)
    +    for cert in all_certs:
    +        if cert["name_value"] == domain:
    +            return cert
    +
    +
    +
    +def has_reached_limit(domain, limit=50) +
    +
    +

    check if a domain has reached the rate limit for issues certs

    +

    Args

    +
    +
    domain : str
    +
    parent domain
    +
    limit : int
    +
    limit to be checked against. defaults to 50
    +
    +

    Returns

    +
    +
    bool
    +
    True if the limit has been reached
    +
    +

    Raises

    +

    requests.exceptions.HTTPError

    +
    + +Expand source code + +
    def has_reached_limit(domain, limit=RATE_LIMIT):
    +    """check if a domain has reached the rate limit for issues certs
    +
    +    Args:
    +        domain (str): parent domain
    +        limit (int): limit to be checked against. defaults to 50
    +
    +    Returns:
    +        bool: True if the limit has been reached
    +
    +    Raises:
    +        requests.exceptions.HTTPError
    +    """
    +    count = count_domain_certs_since(domain)
    +    return count >= limit
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/fs/index.html b/docs/api/jumpscale/sals/fs/index.html index 725c99bfd..a608efb24 100644 --- a/docs/api/jumpscale/sals/fs/index.html +++ b/docs/api/jumpscale/sals/fs/index.html @@ -3815,4 +3815,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/hostsfile/index.html b/docs/api/jumpscale/sals/hostsfile/index.html index ead294107..074a54949 100644 --- a/docs/api/jumpscale/sals/hostsfile/index.html +++ b/docs/api/jumpscale/sals/hostsfile/index.html @@ -81,7 +81,7 @@

    Module jumpscale.sals.hostsfile

    update the hostname for ip Args: ip (str) : the ip address - domain (str) : the host name + domain (str) : the host name """ self.content[ip] = domain @@ -177,7 +177,7 @@

    Classes

    update the hostname for ip Args: ip (str) : the ip address - domain (str) : the host name + domain (str) : the host name """ self.content[ip] = domain @@ -310,7 +310,7 @@

    Args

    update the hostname for ip Args: ip (str) : the ip address - domain (str) : the host name + domain (str) : the host name """ self.content[ip] = domain
    @@ -373,4 +373,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/index.html b/docs/api/jumpscale/sals/index.html index 5d6ddb85b..0c4b6b6a6 100644 --- a/docs/api/jumpscale/sals/index.html +++ b/docs/api/jumpscale/sals/index.html @@ -26,6 +26,22 @@

    Namespace jumpscale.sals

    Sub-modules

    +
    jumpscale.sals.backupjob
    +
    +
    +
    +
    jumpscale.sals.billing
    +
    +
    +
    +
    jumpscale.sals.chatflows
    +
    +
    +
    +
    jumpscale.sals.crtsh
    +
    +
    +
    jumpscale.sals.fs

    This module is providing everything needed for decent filesystem management @@ -39,6 +55,14 @@

    Sub-modules

    This module contains a collection of functions which help in manage network connections and interfaces …

    +
    jumpscale.sals.nginx
    +
    +
    +
    +
    jumpscale.sals.nginx_proxy
    +
    +
    +
    jumpscale.sals.process

    This module execute process on system and manage them …

    @@ -73,9 +97,15 @@

    Index

  • Sub-modules

      +
    • jumpscale.sals.backupjob
    • +
    • jumpscale.sals.billing
    • +
    • jumpscale.sals.chatflows
    • +
    • jumpscale.sals.crtsh
    • jumpscale.sals.fs
    • jumpscale.sals.hostsfile
    • jumpscale.sals.nettools
    • +
    • jumpscale.sals.nginx
    • +
    • jumpscale.sals.nginx_proxy
    • jumpscale.sals.process
    • jumpscale.sals.testdocs
    • jumpscale.sals.unix
    • @@ -88,4 +118,4 @@

      Index

      Generated by pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/nettools/index.html b/docs/api/jumpscale/sals/nettools/index.html index 7566febf9..df52f568d 100644 --- a/docs/api/jumpscale/sals/nettools/index.html +++ b/docs/api/jumpscale/sals/nettools/index.html @@ -1922,4 +1922,4 @@

      Index

      Generated by pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/nginx/index.html b/docs/api/jumpscale/sals/nginx/index.html new file mode 100644 index 000000000..3dc61dc5c --- /dev/null +++ b/docs/api/jumpscale/sals/nginx/index.html @@ -0,0 +1,104 @@ + + + + + + +jumpscale.sals.nginx API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.sals.nginx

      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import Factory
      +    from .nginx import NginxConfig
      +
      +    return Factory(NginxConfig)
      +
      +
      +
      +

      Sub-modules

      +
      +
      jumpscale.sals.nginx.nginx
      +
      +

      Get nginx sal instance as an abstract parent for the websites and locations and for the nginx conf …

      +
      +
      jumpscale.sals.nginx.utils
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from jumpscale.core.base import Factory
      +    from .nginx import NginxConfig
      +
      +    return Factory(NginxConfig)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/nginx/nginx.html b/docs/api/jumpscale/sals/nginx/nginx.html new file mode 100644 index 000000000..c82dd8c79 --- /dev/null +++ b/docs/api/jumpscale/sals/nginx/nginx.html @@ -0,0 +1,3631 @@ + + + + + + +jumpscale.sals.nginx.nginx API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.sals.nginx.nginx

      +
      +
      +

      Get nginx sal instance as an abstract parent for the websites and locations and for the nginx conf

      +

      nginx = j.sals.nginx.get("instance")

      +

      Add a webiste to the configuration

      +

      website = nginx.websites.new("website")

      +

      Configure instance

      +

      website.port = +# +Port to listen to will use 80 by default or 443 if ssl is true

      +

      webiste.ssl = True +# +If true will generate and configure the SSL certificates

      +

      website.domain = +# +domain of the webiste

      +

      website.letsencryptemail = +# +Email to receive let's encrypt notifications

      +

      Add locations to the webiste

      +

      loc = website.locations.new("location")

      +

      loc.path_url = +# +location path +loc.is_auth = +# +IF True will authenticate when accessing this location +loc.force_https = +# +If True won't allow http access +loc.path_location = # +alias for the location +loc.index = # +index of the location +loc.ipaddr_dest = # +Destination address for proxy pass +loc.port_dest = # +Destination port for proxy pass +loc.path_dest = # +Destination path for proxy pass +loc.location_type = +# static,spa,proxy type of location config +loc.scheme = # +https or https

      +

      Configuring the website and all its locations and generating the certificates

      +

      website.configure()

      +
      + +Expand source code + +
      """
      +#  Get nginx sal instance as an abstract parent for the websites and locations and for the nginx conf
      +
      +nginx = j.sals.nginx.get("instance")
      +
      +#  Add a webiste to the configuration
      +
      +website = nginx.websites.new("website")
      +
      +# Configure instance
      +
      +website.port =  #  Port to listen to will use 80 by default or 443 if ssl is true
      +
      +webiste.ssl = True  #  If true will generate and configure the SSL certificates
      +
      +website.domain =  #  domain of the webiste
      +
      +website.letsencryptemail =  #  Email to receive let's encrypt notifications
      +
      +# Add locations to the webiste
      +
      +loc = website.locations.new("location")
      +
      +loc.path_url =  #  location path
      +loc.is_auth =  #  IF True will authenticate when accessing this location
      +loc.force_https =  #  If True won't allow http access
      +loc.path_location = #  alias for the location
      +loc.index = #  index of the location
      +loc.ipaddr_dest = #  Destination address for proxy pass
      +loc.port_dest = #  Destination port for proxy pass
      +loc.path_dest = #  Destination path for proxy pass
      +loc.location_type =  # static,spa,proxy type of location config
      +loc.scheme = #  https or https
      +
      +#  Configuring the website and all its locations and generating the certificates
      +
      +website.configure()
      +"""
      +
      +from enum import Enum
      +
      +from jumpscale.core.base import Base, fields
      +from jumpscale.core.exceptions import Input
      +from jumpscale.loader import j
      +
      +from .utils import DIR_PATH, render_config_template
      +
      +
      +class PORTS:
      +    HTTP = 80
      +    HTTPS = 443
      +
      +    @classmethod
      +    def init_default_ports(cls, local=False):
      +        if local:
      +            for port in range(8080, 8180):
      +                if not j.sals.process.is_port_listening(port):
      +                    cls.HTTP = port
      +                    break
      +            else:
      +                j.exception.Runtime("Could not find free port to listen on")
      +            for port in range(8443, 8500):
      +                if not j.sals.process.is_port_listening(port):
      +                    cls.HTTPS = port
      +                    break
      +            else:
      +                j.exception.Runtime("Could not find free port to listen on")
      +
      +
      +class ProxyBuffering(Enum):
      +    UNSET = ""
      +    ON = "on"
      +    OFF = "off"
      +
      +
      +class LocationType(Enum):
      +    STATIC = "static"
      +    PROXY = "proxy"
      +    CUSTOM = "custom"
      +
      +
      +class Location(Base):
      +    path_url = fields.String(default="/")
      +    force_https = fields.Boolean(default=False)
      +    path_location = fields.String(default="/")
      +    index = fields.String(default="index.html")
      +
      +    scheme = fields.String(default="http")
      +    host = fields.String(default="127.0.0.1")
      +    port = fields.Integer()
      +    path_dest = fields.String(default="/")
      +    spa = fields.Boolean(default=False)
      +    websocket = fields.Boolean(default=False)
      +    location_type = fields.Enum(LocationType)
      +    is_auth = fields.Boolean(default=False)
      +    is_admin = fields.Boolean(default=False)
      +    package_name = fields.String()
      +    is_package_authorized = fields.Boolean(default=False)
      +    custom_config = fields.String(default=None)
      +    proxy_buffering = fields.Enum(ProxyBuffering)
      +    proxy_buffers = fields.String()
      +    proxy_buffer_size = fields.String()
      +
      +    @property
      +    def cfg_dir(self):
      +        return j.sals.fs.join_paths(self.parent.cfg_dir, "locations")
      +
      +    @property
      +    def cfg_file(self):
      +        return j.sals.fs.join_paths(self.cfg_dir, f"{self.instance_name}.conf")
      +
      +    def get_config(self):
      +        return render_config_template(
      +            "location",
      +            base_dir=j.core.dirs.BASEDIR,
      +            location=self,
      +            threebot_connect=j.core.config.get_config().get("threebot_connect", True),
      +            https_port=PORTS.HTTPS,
      +        )
      +
      +    def configure(self):
      +        j.sals.fs.mkdir(self.cfg_dir)
      +        j.sals.fs.write_file(self.cfg_file, self.get_config())
      +
      +
      +class AcmeServer(Enum):
      +    LETSENCRYPT = "letsencrypt"
      +    ZEROSSL = "zerossl"
      +    CUSTOM = "custom"
      +
      +
      +class Certbot(Base):
      +    DEFAULT_NAME = "certbot"
      +    DEFAULT_LOGS_DIR = j.sals.fs.join_paths(j.core.dirs.LOGDIR, DEFAULT_NAME)
      +    DEFAULT_CONFIG_DIR = j.sals.fs.join_paths(j.core.dirs.CFGDIR, DEFAULT_NAME)
      +    DEFAULT_WORK_DIR = j.sals.fs.join_paths(j.core.dirs.VARDIR, DEFAULT_NAME)
      +
      +    # the following options match the certbot command arguments
      +    domain = fields.String(required=True)
      +    non_interactive = fields.Boolean(default=True)
      +    agree_tos = fields.Boolean(default=True)
      +    logs_dir = fields.String(default=DEFAULT_LOGS_DIR)
      +    config_dir = fields.String(default=DEFAULT_CONFIG_DIR)
      +    work_dir = fields.String(default=DEFAULT_WORK_DIR)
      +
      +    email = fields.Email()
      +    server = fields.URL()
      +    eab_kid = fields.String()
      +    eab_hmac_key = fields.String()
      +
      +    # for existing certificates
      +    key_path = fields.String()
      +    cert_path = fields.String()
      +    fullchain_path = fields.String()
      +
      +    @property
      +    def run_cmd(self):
      +        args = [self.DEFAULT_NAME]
      +
      +        for name, value in self.to_dict().items():
      +            if name.endswith("_"):
      +                continue
      +
      +            if value:
      +                # append only if the field has a value
      +                name = name.replace("_", "-")
      +                args.append(f"--{name}")
      +
      +                # append the value itself only if it's a boolean value
      +                # boolean options are set by adding name only
      +                if not isinstance(value, bool):
      +                    args.append(value)
      +
      +        return args
      +
      +    @property
      +    def install_cmd(self):
      +        # replace "certbot" with "certbot install"
      +        cmd = self.run_cmd
      +        cmd.insert(1, "install")
      +        return cmd
      +
      +    @property
      +    def renew_cmd(self):
      +        # replace "certbot" with "certbot install"
      +        renew_certbot = Certbot(work_dir=self.work_dir, config_dir=self.config_dir, logs_dir=self.logs_dir, domain="")
      +        cmd = renew_certbot.run_cmd
      +        cmd.insert(1, "renew")
      +        return cmd
      +
      +
      +class NginxCertbot(Certbot):
      +    nginx_server_root = fields.String(required=True)
      +    nginx = fields.Boolean(default=True)
      +
      +
      +class LetsencryptCertbot(NginxCertbot):
      +    """
      +    default installation is for let's encrypt (manual plugin), no need for other options
      +
      +    currently, we support only email
      +    """
      +
      +    # change required value to True here
      +    email = fields.Email(required=True)
      +
      +
      +class ZerosslCertbot(NginxCertbot):
      +    SERVER_URL = "https://acme.zerossl.com/v2/DV90"
      +    KEY_CREDENTIALS_URL = "https://api.zerossl.com/acme/eab-credentials"
      +    EMAIL_CREDENTIALS_URL = "https://api.zerossl.com/acme/eab-credentials-email"
      +
      +    api_key_ = fields.Secret()
      +    server = fields.URL(default=SERVER_URL)
      +
      +    @property
      +    def run_cmd(self):
      +        # get eab_kid and eab_hmac_key based on email or api_key_
      +        if not self.email and not self.api_key_:
      +            raise Input("email or api_key_ must be provided")
      +
      +        # set them to get the full run-cmd with correct arguments
      +        if self.api_key_:
      +            resp = j.tools.http.post(self.KEY_CREDENTIALS_URL, params={"access_key": self.api_key_})
      +        else:
      +            resp = j.tools.http.post(self.EMAIL_CREDENTIALS_URL, data={"email": self.email})
      +
      +        resp.raise_for_status()
      +        data = resp.json()
      +
      +        self.eab_kid = data["eab_kid"]
      +        self.eab_hmac_key = data["eab_hmac_key"]
      +
      +        return super().run_cmd
      +
      +
      +class CustomCertbot(NginxCertbot):
      +    # change email and server required value to True here
      +    email = fields.Email(required=True)
      +    server = fields.URL(required=True)
      +
      +
      +class Website(Base):
      +    domain = fields.String()
      +    ssl = fields.Boolean()
      +    port = fields.Integer(default=PORTS.HTTP)
      +    locations = fields.Factory(Location, stored=False)
      +    includes = fields.List(fields.String())
      +
      +    selfsigned = fields.Boolean(default=True)
      +
      +    # keep it as letsencryptemail for compatibility
      +    letsencryptemail = fields.String()
      +    acme_server_type = fields.Enum(AcmeServer)
      +    acme_server_url = fields.URL()
      +    # in case of using existing key/certificate
      +    key_path = fields.String()
      +    cert_path = fields.String()
      +    fullchain_path = fields.String()
      +
      +    @property
      +    def certbot(self):
      +        kwargs = dict(
      +            domain=self.domain,
      +            email=self.letsencryptemail,
      +            server=self.acme_server_url,
      +            nginx_server_root=self.parent.cfg_dir,
      +            key_path=self.key_path,
      +            cert_path=self.cert_path,
      +            fullchain_path=self.fullchain_path,
      +        )
      +
      +        if self.acme_server_type == AcmeServer.LETSENCRYPT:
      +            certbot_type = LetsencryptCertbot
      +        elif self.acme_server_type == AcmeServer.ZEROSSL:
      +            certbot_type = ZerosslCertbot
      +        else:
      +            certbot_type = CustomCertbot
      +
      +        return certbot_type(**kwargs)
      +
      +    @property
      +    def cfg_dir(self):
      +        return j.sals.fs.join_paths(self.parent.cfg_dir, self.instance_name)
      +
      +    @property
      +    def cfg_file(self):
      +        return j.sals.fs.join_paths(self.cfg_dir, "server.conf")
      +
      +    @property
      +    def include_paths(self):
      +        paths = []
      +        for include in self.includes:
      +            ## TODO validate location name and include
      +            website_name, location_name = include.split(".", 1)
      +            website = self.parent.websites.find(website_name)
      +            if not website:
      +                continue
      +
      +            paths.append(j.sals.fs.join_paths(website.cfg_dir, "locations", location_name))
      +        return paths
      +
      +    def get_locations(self):
      +        for location in self.locations.list_all():
      +            yield self.locations.get(location)
      +
      +    def get_proxy_location(self, name):
      +        location = self.locations.get(name)
      +        location.location_type = LocationType.PROXY
      +        return location
      +
      +    def get_custom_location(self, name):
      +        location = self.locations.get(name)
      +        location.location_type = LocationType.CUSTOM
      +        return location
      +
      +    def get_static_location(self, name):
      +        location = self.locations.get(name)
      +        location.location_type = LocationType.STATIC
      +        return location
      +
      +    def get_config(self):
      +        return render_config_template("website", base_dir=j.core.dirs.BASEDIR, website=self)
      +
      +    def generate_certificates(self, retries=6):
      +        if self.domain:
      +            if self.key_path and self.cert_path and self.fullchain_path:
      +                # only use install command if an existing key and certificate were set
      +                self.install_certifcate()
      +            else:
      +                self.obtain_and_install_certifcate(retries=retries)
      +
      +    def install_certifcate(self):
      +        """Construct and Execute install certificate command
      +        Alternative to certbot install
      +
      +        """
      +        cmd = self.certbot.install_cmd
      +        j.logger.debug(f"Execute: {' '.join(cmd)}")
      +        rc, out, err = j.sals.process.execute(cmd)
      +        if rc > 0:
      +            j.logger.error(f"Installing certificate failed {out}\n{err}")
      +        else:
      +            j.logger.info(f"Certificate installed successfully {out}")
      +
      +    def obtain_and_install_certifcate(self, retries=6):
      +        """Construct and Execute run certificate command,This will issue a new certificate managed by Certbot
      +        Alternative to certbot run
      +
      +        Args:
      +            retries (int, optional): Number of retries Certbot will try to install the certificate if failed. Defaults to 6.
      +        """
      +        cmd = self.certbot.run_cmd
      +        j.logger.debug(f"Execute: {' '.join(cmd)}")
      +        for _ in range(retries):
      +            rc, out, err = j.sals.process.execute(cmd)
      +            if rc > 0:
      +                j.logger.error(f"Generating certificate failed {out}\n{err}")
      +            else:
      +                j.logger.error(f"Certificate Generated successfully {out}")
      +                break
      +
      +    def generate_self_signed_certificates(self):
      +        keypempath = f"{self.parent.cfg_dir}/key.pem"
      +        certpempath = f"{self.parent.cfg_dir}/cert.pem"
      +        if j.sals.process.is_installed("mkcert"):
      +            res = j.sals.process.execute(
      +                f"mkcert -key-file {keypempath} -cert-file {certpempath} localhost *.localhost 127.0.0.1 ::1"
      +            )
      +            if res[0] != 0:
      +                raise j.exceptions.JSException(f"Failed to generate self-signed certificate (using mkcert).{res}")
      +
      +        else:
      +            if j.sals.fs.exists(f"{keypempath}") and j.sals.fs.exists(f"{certpempath}"):
      +                return
      +            res = j.sals.process.execute(
      +                f"openssl req -nodes -x509 -newkey rsa:4096 -keyout {keypempath} -out {certpempath} -days 365 -subj '/CN=localhost'"
      +            )
      +            if res[0] != 0:
      +                raise j.exceptions.JSException(f"Failed to generate self-signed certificate (using openssl).{res}")
      +
      +    def configure(self, generate_certificates=True):
      +        j.sals.fs.mkdir(self.cfg_dir)
      +        needed_dirs = ("body", "client-body", "fastcgi", "proxy", "scgi", "uwsgi")
      +        for d in needed_dirs:
      +            j.sals.fs.mkdir(j.sals.fs.join_paths(self.cfg_dir, d))
      +        for location in self.get_locations():
      +            location.configure()
      +
      +        j.sals.fs.write_file(self.cfg_file, self.get_config())
      +        if self.ssl:
      +            self.generate_self_signed_certificates()
      +        if generate_certificates and self.ssl:
      +            self.generate_certificates()
      +
      +    def clean(self):
      +        j.sals.fs.rmtree(self.cfg_dir)
      +
      +
      +class NginxConfig(Base):
      +    websites = fields.Factory(Website, stored=False)
      +    cert = fields.Boolean(default=True)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._cmd = None
      +        self._path_web = None
      +        self._cfg_dir = None
      +        self._logs_dir = None
      +
      +    @property
      +    def cfg_dir(self):
      +        if not self._cfg_dir:
      +            self._cfg_dir = j.sals.fs.join_paths(j.core.dirs.CFGDIR, "nginx", self.instance_name)
      +            j.sals.fs.mkdirs(self._cfg_dir)
      +        return self._cfg_dir
      +
      +    @property
      +    def cfg_file(self):
      +        return j.sals.fs.join_paths(self.cfg_dir, "nginx.conf")
      +
      +    @property
      +    def logs_dir(self):
      +        if not self._logs_dir:
      +            self._logs_dir = j.sals.fs.join_paths(j.core.dirs.LOGDIR, "nginx", self.instance_name)
      +            j.sals.fs.mkdirs(self._logs_dir)
      +        return self._logs_dir
      +
      +    def configure(self):
      +        """configures main nginx conf"""
      +        self.clean()
      +        j.sals.fs.mkdir(self.cfg_dir)
      +        user = j.sals.unix.get_current_pwd()
      +        group = j.sals.unix.get_current_grp()
      +        def_index_dir = j.sals.fs.join_paths(DIR_PATH, "static")
      +
      +        configtext = j.tools.jinja2.render_template(
      +            template_path=j.sals.fs.join_paths(DIR_PATH, "templates", "nginx.conf"),
      +            logs_dir=self.logs_dir,
      +            cfg_dir=self.cfg_dir,
      +            user=user,
      +            group=group,
      +            def_index_dir=def_index_dir,
      +        )
      +
      +        j.sals.fs.write_file(self.cfg_file, configtext)
      +        j.sals.fs.copy_tree(f"{DIR_PATH}/resources/", self.cfg_dir)
      +
      +    def get_website(self, name: str, port: int = 0):
      +        port = port or PORTS.HTTP
      +        website_name = f"{name}_{port}"
      +        website = self.websites.find(website_name)
      +        if website:
      +            return website
      +
      +        website = self.websites.get(website_name)
      +        website.port = port
      +        website.ssl = port in [443, 8443]
      +        return website
      +
      +    def clean(self):
      +        j.sals.fs.rmtree(f"{self.cfg_dir}")
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AcmeServer +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
      +
      +

      An enumeration.

      +
      + +Expand source code + +
      class AcmeServer(Enum):
      +    LETSENCRYPT = "letsencrypt"
      +    ZEROSSL = "zerossl"
      +    CUSTOM = "custom"
      +
      +

      Ancestors

      +
        +
      • enum.Enum
      • +
      +

      Class variables

      +
      +
      var CUSTOM
      +
      +
      +
      +
      var LETSENCRYPT
      +
      +
      +
      +
      var ZEROSSL
      +
      +
      +
      +
      +
      +
      +class Certbot +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class Certbot(Base):
      +    DEFAULT_NAME = "certbot"
      +    DEFAULT_LOGS_DIR = j.sals.fs.join_paths(j.core.dirs.LOGDIR, DEFAULT_NAME)
      +    DEFAULT_CONFIG_DIR = j.sals.fs.join_paths(j.core.dirs.CFGDIR, DEFAULT_NAME)
      +    DEFAULT_WORK_DIR = j.sals.fs.join_paths(j.core.dirs.VARDIR, DEFAULT_NAME)
      +
      +    # the following options match the certbot command arguments
      +    domain = fields.String(required=True)
      +    non_interactive = fields.Boolean(default=True)
      +    agree_tos = fields.Boolean(default=True)
      +    logs_dir = fields.String(default=DEFAULT_LOGS_DIR)
      +    config_dir = fields.String(default=DEFAULT_CONFIG_DIR)
      +    work_dir = fields.String(default=DEFAULT_WORK_DIR)
      +
      +    email = fields.Email()
      +    server = fields.URL()
      +    eab_kid = fields.String()
      +    eab_hmac_key = fields.String()
      +
      +    # for existing certificates
      +    key_path = fields.String()
      +    cert_path = fields.String()
      +    fullchain_path = fields.String()
      +
      +    @property
      +    def run_cmd(self):
      +        args = [self.DEFAULT_NAME]
      +
      +        for name, value in self.to_dict().items():
      +            if name.endswith("_"):
      +                continue
      +
      +            if value:
      +                # append only if the field has a value
      +                name = name.replace("_", "-")
      +                args.append(f"--{name}")
      +
      +                # append the value itself only if it's a boolean value
      +                # boolean options are set by adding name only
      +                if not isinstance(value, bool):
      +                    args.append(value)
      +
      +        return args
      +
      +    @property
      +    def install_cmd(self):
      +        # replace "certbot" with "certbot install"
      +        cmd = self.run_cmd
      +        cmd.insert(1, "install")
      +        return cmd
      +
      +    @property
      +    def renew_cmd(self):
      +        # replace "certbot" with "certbot install"
      +        renew_certbot = Certbot(work_dir=self.work_dir, config_dir=self.config_dir, logs_dir=self.logs_dir, domain="")
      +        cmd = renew_certbot.run_cmd
      +        cmd.insert(1, "renew")
      +        return cmd
      +
      +

      Ancestors

      +
        +
      • Base
      • +
      • types.SimpleNamespace
      • +
      +

      Subclasses

      + +

      Class variables

      +
      +
      var DEFAULT_CONFIG_DIR
      +
      +
      +
      +
      var DEFAULT_LOGS_DIR
      +
      +
      +
      +
      var DEFAULT_NAME
      +
      +
      +
      +
      var DEFAULT_WORK_DIR
      +
      +
      +
      +
      +

      Instance variables

      +
      +
      var agree_tos
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var cert_path
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var config_dir
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var domain
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var eab_hmac_key
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var eab_kid
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var email
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var fullchain_path
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var install_cmd
      +
      +
      +
      + +Expand source code + +
      @property
      +def install_cmd(self):
      +    # replace "certbot" with "certbot install"
      +    cmd = self.run_cmd
      +    cmd.insert(1, "install")
      +    return cmd
      +
      +
      +
      var key_path
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var logs_dir
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var non_interactive
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var renew_cmd
      +
      +
      +
      + +Expand source code + +
      @property
      +def renew_cmd(self):
      +    # replace "certbot" with "certbot install"
      +    renew_certbot = Certbot(work_dir=self.work_dir, config_dir=self.config_dir, logs_dir=self.logs_dir, domain="")
      +    cmd = renew_certbot.run_cmd
      +    cmd.insert(1, "renew")
      +    return cmd
      +
      +
      +
      var run_cmd
      +
      +
      +
      + +Expand source code + +
      @property
      +def run_cmd(self):
      +    args = [self.DEFAULT_NAME]
      +
      +    for name, value in self.to_dict().items():
      +        if name.endswith("_"):
      +            continue
      +
      +        if value:
      +            # append only if the field has a value
      +            name = name.replace("_", "-")
      +            args.append(f"--{name}")
      +
      +            # append the value itself only if it's a boolean value
      +            # boolean options are set by adding name only
      +            if not isinstance(value, bool):
      +                args.append(value)
      +
      +    return args
      +
      +
      +
      var server
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var work_dir
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class CustomCertbot +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class CustomCertbot(NginxCertbot):
      +    # change email and server required value to True here
      +    email = fields.Email(required=True)
      +    server = fields.URL(required=True)
      +
      +

      Ancestors

      + +

      Inherited members

      + +
      +
      +class LetsencryptCertbot +(parent_=None, instance_name_=None, **values) +
      +
      +

      default installation is for let's encrypt (manual plugin), no need for other options

      +

      currently, we support only email

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class LetsencryptCertbot(NginxCertbot):
      +    """
      +    default installation is for let's encrypt (manual plugin), no need for other options
      +
      +    currently, we support only email
      +    """
      +
      +    # change required value to True here
      +    email = fields.Email(required=True)
      +
      +

      Ancestors

      + +

      Inherited members

      + +
      +
      +class Location +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class Location(Base):
      +    path_url = fields.String(default="/")
      +    force_https = fields.Boolean(default=False)
      +    path_location = fields.String(default="/")
      +    index = fields.String(default="index.html")
      +
      +    scheme = fields.String(default="http")
      +    host = fields.String(default="127.0.0.1")
      +    port = fields.Integer()
      +    path_dest = fields.String(default="/")
      +    spa = fields.Boolean(default=False)
      +    websocket = fields.Boolean(default=False)
      +    location_type = fields.Enum(LocationType)
      +    is_auth = fields.Boolean(default=False)
      +    is_admin = fields.Boolean(default=False)
      +    package_name = fields.String()
      +    is_package_authorized = fields.Boolean(default=False)
      +    custom_config = fields.String(default=None)
      +    proxy_buffering = fields.Enum(ProxyBuffering)
      +    proxy_buffers = fields.String()
      +    proxy_buffer_size = fields.String()
      +
      +    @property
      +    def cfg_dir(self):
      +        return j.sals.fs.join_paths(self.parent.cfg_dir, "locations")
      +
      +    @property
      +    def cfg_file(self):
      +        return j.sals.fs.join_paths(self.cfg_dir, f"{self.instance_name}.conf")
      +
      +    def get_config(self):
      +        return render_config_template(
      +            "location",
      +            base_dir=j.core.dirs.BASEDIR,
      +            location=self,
      +            threebot_connect=j.core.config.get_config().get("threebot_connect", True),
      +            https_port=PORTS.HTTPS,
      +        )
      +
      +    def configure(self):
      +        j.sals.fs.mkdir(self.cfg_dir)
      +        j.sals.fs.write_file(self.cfg_file, self.get_config())
      +
      +

      Ancestors

      +
        +
      • Base
      • +
      • types.SimpleNamespace
      • +
      +

      Instance variables

      +
      +
      var cfg_dir
      +
      +
      +
      + +Expand source code + +
      @property
      +def cfg_dir(self):
      +    return j.sals.fs.join_paths(self.parent.cfg_dir, "locations")
      +
      +
      +
      var cfg_file
      +
      +
      +
      + +Expand source code + +
      @property
      +def cfg_file(self):
      +    return j.sals.fs.join_paths(self.cfg_dir, f"{self.instance_name}.conf")
      +
      +
      +
      var custom_config
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var force_https
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var host
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var index
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var is_admin
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var is_auth
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var is_package_authorized
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var location_type
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var package_name
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var path_dest
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var path_location
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var path_url
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var port
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var proxy_buffer_size
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var proxy_buffering
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var proxy_buffers
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var scheme
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var spa
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var websocket
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def configure(self) +
      +
      +
      +
      + +Expand source code + +
      def configure(self):
      +    j.sals.fs.mkdir(self.cfg_dir)
      +    j.sals.fs.write_file(self.cfg_file, self.get_config())
      +
      +
      +
      +def get_config(self) +
      +
      +
      +
      + +Expand source code + +
      def get_config(self):
      +    return render_config_template(
      +        "location",
      +        base_dir=j.core.dirs.BASEDIR,
      +        location=self,
      +        threebot_connect=j.core.config.get_config().get("threebot_connect", True),
      +        https_port=PORTS.HTTPS,
      +    )
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class LocationType +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
      +
      +

      An enumeration.

      +
      + +Expand source code + +
      class LocationType(Enum):
      +    STATIC = "static"
      +    PROXY = "proxy"
      +    CUSTOM = "custom"
      +
      +

      Ancestors

      +
        +
      • enum.Enum
      • +
      +

      Class variables

      +
      +
      var CUSTOM
      +
      +
      +
      +
      var PROXY
      +
      +
      +
      +
      var STATIC
      +
      +
      +
      +
      +
      +
      +class NginxCertbot +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class NginxCertbot(Certbot):
      +    nginx_server_root = fields.String(required=True)
      +    nginx = fields.Boolean(default=True)
      +
      +

      Ancestors

      + +

      Subclasses

      + +

      Instance variables

      +
      +
      var nginx
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var nginx_server_root
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class NginxConfig +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class NginxConfig(Base):
      +    websites = fields.Factory(Website, stored=False)
      +    cert = fields.Boolean(default=True)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._cmd = None
      +        self._path_web = None
      +        self._cfg_dir = None
      +        self._logs_dir = None
      +
      +    @property
      +    def cfg_dir(self):
      +        if not self._cfg_dir:
      +            self._cfg_dir = j.sals.fs.join_paths(j.core.dirs.CFGDIR, "nginx", self.instance_name)
      +            j.sals.fs.mkdirs(self._cfg_dir)
      +        return self._cfg_dir
      +
      +    @property
      +    def cfg_file(self):
      +        return j.sals.fs.join_paths(self.cfg_dir, "nginx.conf")
      +
      +    @property
      +    def logs_dir(self):
      +        if not self._logs_dir:
      +            self._logs_dir = j.sals.fs.join_paths(j.core.dirs.LOGDIR, "nginx", self.instance_name)
      +            j.sals.fs.mkdirs(self._logs_dir)
      +        return self._logs_dir
      +
      +    def configure(self):
      +        """configures main nginx conf"""
      +        self.clean()
      +        j.sals.fs.mkdir(self.cfg_dir)
      +        user = j.sals.unix.get_current_pwd()
      +        group = j.sals.unix.get_current_grp()
      +        def_index_dir = j.sals.fs.join_paths(DIR_PATH, "static")
      +
      +        configtext = j.tools.jinja2.render_template(
      +            template_path=j.sals.fs.join_paths(DIR_PATH, "templates", "nginx.conf"),
      +            logs_dir=self.logs_dir,
      +            cfg_dir=self.cfg_dir,
      +            user=user,
      +            group=group,
      +            def_index_dir=def_index_dir,
      +        )
      +
      +        j.sals.fs.write_file(self.cfg_file, configtext)
      +        j.sals.fs.copy_tree(f"{DIR_PATH}/resources/", self.cfg_dir)
      +
      +    def get_website(self, name: str, port: int = 0):
      +        port = port or PORTS.HTTP
      +        website_name = f"{name}_{port}"
      +        website = self.websites.find(website_name)
      +        if website:
      +            return website
      +
      +        website = self.websites.get(website_name)
      +        website.port = port
      +        website.ssl = port in [443, 8443]
      +        return website
      +
      +    def clean(self):
      +        j.sals.fs.rmtree(f"{self.cfg_dir}")
      +
      +

      Ancestors

      +
        +
      • Base
      • +
      • types.SimpleNamespace
      • +
      +

      Instance variables

      +
      +
      var cert
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var cfg_dir
      +
      +
      +
      + +Expand source code + +
      @property
      +def cfg_dir(self):
      +    if not self._cfg_dir:
      +        self._cfg_dir = j.sals.fs.join_paths(j.core.dirs.CFGDIR, "nginx", self.instance_name)
      +        j.sals.fs.mkdirs(self._cfg_dir)
      +    return self._cfg_dir
      +
      +
      +
      var cfg_file
      +
      +
      +
      + +Expand source code + +
      @property
      +def cfg_file(self):
      +    return j.sals.fs.join_paths(self.cfg_dir, "nginx.conf")
      +
      +
      +
      var logs_dir
      +
      +
      +
      + +Expand source code + +
      @property
      +def logs_dir(self):
      +    if not self._logs_dir:
      +        self._logs_dir = j.sals.fs.join_paths(j.core.dirs.LOGDIR, "nginx", self.instance_name)
      +        j.sals.fs.mkdirs(self._logs_dir)
      +    return self._logs_dir
      +
      +
      +
      var websites
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def clean(self) +
      +
      +
      +
      + +Expand source code + +
      def clean(self):
      +    j.sals.fs.rmtree(f"{self.cfg_dir}")
      +
      +
      +
      +def configure(self) +
      +
      +

      configures main nginx conf

      +
      + +Expand source code + +
      def configure(self):
      +    """configures main nginx conf"""
      +    self.clean()
      +    j.sals.fs.mkdir(self.cfg_dir)
      +    user = j.sals.unix.get_current_pwd()
      +    group = j.sals.unix.get_current_grp()
      +    def_index_dir = j.sals.fs.join_paths(DIR_PATH, "static")
      +
      +    configtext = j.tools.jinja2.render_template(
      +        template_path=j.sals.fs.join_paths(DIR_PATH, "templates", "nginx.conf"),
      +        logs_dir=self.logs_dir,
      +        cfg_dir=self.cfg_dir,
      +        user=user,
      +        group=group,
      +        def_index_dir=def_index_dir,
      +    )
      +
      +    j.sals.fs.write_file(self.cfg_file, configtext)
      +    j.sals.fs.copy_tree(f"{DIR_PATH}/resources/", self.cfg_dir)
      +
      +
      +
      +def get_website(self, name: str, port: int = 0) +
      +
      +
      +
      + +Expand source code + +
      def get_website(self, name: str, port: int = 0):
      +    port = port or PORTS.HTTP
      +    website_name = f"{name}_{port}"
      +    website = self.websites.find(website_name)
      +    if website:
      +        return website
      +
      +    website = self.websites.get(website_name)
      +    website.port = port
      +    website.ssl = port in [443, 8443]
      +    return website
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class PORTS +
      +
      +
      +
      + +Expand source code + +
      class PORTS:
      +    HTTP = 80
      +    HTTPS = 443
      +
      +    @classmethod
      +    def init_default_ports(cls, local=False):
      +        if local:
      +            for port in range(8080, 8180):
      +                if not j.sals.process.is_port_listening(port):
      +                    cls.HTTP = port
      +                    break
      +            else:
      +                j.exception.Runtime("Could not find free port to listen on")
      +            for port in range(8443, 8500):
      +                if not j.sals.process.is_port_listening(port):
      +                    cls.HTTPS = port
      +                    break
      +            else:
      +                j.exception.Runtime("Could not find free port to listen on")
      +
      +

      Class variables

      +
      +
      var HTTP
      +
      +
      +
      +
      var HTTPS
      +
      +
      +
      +
      +

      Static methods

      +
      +
      +def init_default_ports(local=False) +
      +
      +
      +
      + +Expand source code + +
      @classmethod
      +def init_default_ports(cls, local=False):
      +    if local:
      +        for port in range(8080, 8180):
      +            if not j.sals.process.is_port_listening(port):
      +                cls.HTTP = port
      +                break
      +        else:
      +            j.exception.Runtime("Could not find free port to listen on")
      +        for port in range(8443, 8500):
      +            if not j.sals.process.is_port_listening(port):
      +                cls.HTTPS = port
      +                break
      +        else:
      +            j.exception.Runtime("Could not find free port to listen on")
      +
      +
      +
      +
      +
      +class ProxyBuffering +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
      +
      +

      An enumeration.

      +
      + +Expand source code + +
      class ProxyBuffering(Enum):
      +    UNSET = ""
      +    ON = "on"
      +    OFF = "off"
      +
      +

      Ancestors

      +
        +
      • enum.Enum
      • +
      +

      Class variables

      +
      +
      var OFF
      +
      +
      +
      +
      var ON
      +
      +
      +
      +
      var UNSET
      +
      +
      +
      +
      +
      +
      +class Website +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class Website(Base):
      +    domain = fields.String()
      +    ssl = fields.Boolean()
      +    port = fields.Integer(default=PORTS.HTTP)
      +    locations = fields.Factory(Location, stored=False)
      +    includes = fields.List(fields.String())
      +
      +    selfsigned = fields.Boolean(default=True)
      +
      +    # keep it as letsencryptemail for compatibility
      +    letsencryptemail = fields.String()
      +    acme_server_type = fields.Enum(AcmeServer)
      +    acme_server_url = fields.URL()
      +    # in case of using existing key/certificate
      +    key_path = fields.String()
      +    cert_path = fields.String()
      +    fullchain_path = fields.String()
      +
      +    @property
      +    def certbot(self):
      +        kwargs = dict(
      +            domain=self.domain,
      +            email=self.letsencryptemail,
      +            server=self.acme_server_url,
      +            nginx_server_root=self.parent.cfg_dir,
      +            key_path=self.key_path,
      +            cert_path=self.cert_path,
      +            fullchain_path=self.fullchain_path,
      +        )
      +
      +        if self.acme_server_type == AcmeServer.LETSENCRYPT:
      +            certbot_type = LetsencryptCertbot
      +        elif self.acme_server_type == AcmeServer.ZEROSSL:
      +            certbot_type = ZerosslCertbot
      +        else:
      +            certbot_type = CustomCertbot
      +
      +        return certbot_type(**kwargs)
      +
      +    @property
      +    def cfg_dir(self):
      +        return j.sals.fs.join_paths(self.parent.cfg_dir, self.instance_name)
      +
      +    @property
      +    def cfg_file(self):
      +        return j.sals.fs.join_paths(self.cfg_dir, "server.conf")
      +
      +    @property
      +    def include_paths(self):
      +        paths = []
      +        for include in self.includes:
      +            ## TODO validate location name and include
      +            website_name, location_name = include.split(".", 1)
      +            website = self.parent.websites.find(website_name)
      +            if not website:
      +                continue
      +
      +            paths.append(j.sals.fs.join_paths(website.cfg_dir, "locations", location_name))
      +        return paths
      +
      +    def get_locations(self):
      +        for location in self.locations.list_all():
      +            yield self.locations.get(location)
      +
      +    def get_proxy_location(self, name):
      +        location = self.locations.get(name)
      +        location.location_type = LocationType.PROXY
      +        return location
      +
      +    def get_custom_location(self, name):
      +        location = self.locations.get(name)
      +        location.location_type = LocationType.CUSTOM
      +        return location
      +
      +    def get_static_location(self, name):
      +        location = self.locations.get(name)
      +        location.location_type = LocationType.STATIC
      +        return location
      +
      +    def get_config(self):
      +        return render_config_template("website", base_dir=j.core.dirs.BASEDIR, website=self)
      +
      +    def generate_certificates(self, retries=6):
      +        if self.domain:
      +            if self.key_path and self.cert_path and self.fullchain_path:
      +                # only use install command if an existing key and certificate were set
      +                self.install_certifcate()
      +            else:
      +                self.obtain_and_install_certifcate(retries=retries)
      +
      +    def install_certifcate(self):
      +        """Construct and Execute install certificate command
      +        Alternative to certbot install
      +
      +        """
      +        cmd = self.certbot.install_cmd
      +        j.logger.debug(f"Execute: {' '.join(cmd)}")
      +        rc, out, err = j.sals.process.execute(cmd)
      +        if rc > 0:
      +            j.logger.error(f"Installing certificate failed {out}\n{err}")
      +        else:
      +            j.logger.info(f"Certificate installed successfully {out}")
      +
      +    def obtain_and_install_certifcate(self, retries=6):
      +        """Construct and Execute run certificate command,This will issue a new certificate managed by Certbot
      +        Alternative to certbot run
      +
      +        Args:
      +            retries (int, optional): Number of retries Certbot will try to install the certificate if failed. Defaults to 6.
      +        """
      +        cmd = self.certbot.run_cmd
      +        j.logger.debug(f"Execute: {' '.join(cmd)}")
      +        for _ in range(retries):
      +            rc, out, err = j.sals.process.execute(cmd)
      +            if rc > 0:
      +                j.logger.error(f"Generating certificate failed {out}\n{err}")
      +            else:
      +                j.logger.error(f"Certificate Generated successfully {out}")
      +                break
      +
      +    def generate_self_signed_certificates(self):
      +        keypempath = f"{self.parent.cfg_dir}/key.pem"
      +        certpempath = f"{self.parent.cfg_dir}/cert.pem"
      +        if j.sals.process.is_installed("mkcert"):
      +            res = j.sals.process.execute(
      +                f"mkcert -key-file {keypempath} -cert-file {certpempath} localhost *.localhost 127.0.0.1 ::1"
      +            )
      +            if res[0] != 0:
      +                raise j.exceptions.JSException(f"Failed to generate self-signed certificate (using mkcert).{res}")
      +
      +        else:
      +            if j.sals.fs.exists(f"{keypempath}") and j.sals.fs.exists(f"{certpempath}"):
      +                return
      +            res = j.sals.process.execute(
      +                f"openssl req -nodes -x509 -newkey rsa:4096 -keyout {keypempath} -out {certpempath} -days 365 -subj '/CN=localhost'"
      +            )
      +            if res[0] != 0:
      +                raise j.exceptions.JSException(f"Failed to generate self-signed certificate (using openssl).{res}")
      +
      +    def configure(self, generate_certificates=True):
      +        j.sals.fs.mkdir(self.cfg_dir)
      +        needed_dirs = ("body", "client-body", "fastcgi", "proxy", "scgi", "uwsgi")
      +        for d in needed_dirs:
      +            j.sals.fs.mkdir(j.sals.fs.join_paths(self.cfg_dir, d))
      +        for location in self.get_locations():
      +            location.configure()
      +
      +        j.sals.fs.write_file(self.cfg_file, self.get_config())
      +        if self.ssl:
      +            self.generate_self_signed_certificates()
      +        if generate_certificates and self.ssl:
      +            self.generate_certificates()
      +
      +    def clean(self):
      +        j.sals.fs.rmtree(self.cfg_dir)
      +
      +

      Ancestors

      +
        +
      • Base
      • +
      • types.SimpleNamespace
      • +
      +

      Instance variables

      +
      +
      var acme_server_type
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var acme_server_url
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var cert_path
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var certbot
      +
      +
      +
      + +Expand source code + +
      @property
      +def certbot(self):
      +    kwargs = dict(
      +        domain=self.domain,
      +        email=self.letsencryptemail,
      +        server=self.acme_server_url,
      +        nginx_server_root=self.parent.cfg_dir,
      +        key_path=self.key_path,
      +        cert_path=self.cert_path,
      +        fullchain_path=self.fullchain_path,
      +    )
      +
      +    if self.acme_server_type == AcmeServer.LETSENCRYPT:
      +        certbot_type = LetsencryptCertbot
      +    elif self.acme_server_type == AcmeServer.ZEROSSL:
      +        certbot_type = ZerosslCertbot
      +    else:
      +        certbot_type = CustomCertbot
      +
      +    return certbot_type(**kwargs)
      +
      +
      +
      var cfg_dir
      +
      +
      +
      + +Expand source code + +
      @property
      +def cfg_dir(self):
      +    return j.sals.fs.join_paths(self.parent.cfg_dir, self.instance_name)
      +
      +
      +
      var cfg_file
      +
      +
      +
      + +Expand source code + +
      @property
      +def cfg_file(self):
      +    return j.sals.fs.join_paths(self.cfg_dir, "server.conf")
      +
      +
      +
      var domain
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var fullchain_path
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var include_paths
      +
      +
      +
      + +Expand source code + +
      @property
      +def include_paths(self):
      +    paths = []
      +    for include in self.includes:
      +        ## TODO validate location name and include
      +        website_name, location_name = include.split(".", 1)
      +        website = self.parent.websites.find(website_name)
      +        if not website:
      +            continue
      +
      +        paths.append(j.sals.fs.join_paths(website.cfg_dir, "locations", location_name))
      +    return paths
      +
      +
      +
      var includes
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var key_path
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var letsencryptemail
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var locations
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var port
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var selfsigned
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var ssl
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def clean(self) +
      +
      +
      +
      + +Expand source code + +
      def clean(self):
      +    j.sals.fs.rmtree(self.cfg_dir)
      +
      +
      +
      +def configure(self, generate_certificates=True) +
      +
      +
      +
      + +Expand source code + +
      def configure(self, generate_certificates=True):
      +    j.sals.fs.mkdir(self.cfg_dir)
      +    needed_dirs = ("body", "client-body", "fastcgi", "proxy", "scgi", "uwsgi")
      +    for d in needed_dirs:
      +        j.sals.fs.mkdir(j.sals.fs.join_paths(self.cfg_dir, d))
      +    for location in self.get_locations():
      +        location.configure()
      +
      +    j.sals.fs.write_file(self.cfg_file, self.get_config())
      +    if self.ssl:
      +        self.generate_self_signed_certificates()
      +    if generate_certificates and self.ssl:
      +        self.generate_certificates()
      +
      +
      +
      +def generate_certificates(self, retries=6) +
      +
      +
      +
      + +Expand source code + +
      def generate_certificates(self, retries=6):
      +    if self.domain:
      +        if self.key_path and self.cert_path and self.fullchain_path:
      +            # only use install command if an existing key and certificate were set
      +            self.install_certifcate()
      +        else:
      +            self.obtain_and_install_certifcate(retries=retries)
      +
      +
      +
      +def generate_self_signed_certificates(self) +
      +
      +
      +
      + +Expand source code + +
      def generate_self_signed_certificates(self):
      +    keypempath = f"{self.parent.cfg_dir}/key.pem"
      +    certpempath = f"{self.parent.cfg_dir}/cert.pem"
      +    if j.sals.process.is_installed("mkcert"):
      +        res = j.sals.process.execute(
      +            f"mkcert -key-file {keypempath} -cert-file {certpempath} localhost *.localhost 127.0.0.1 ::1"
      +        )
      +        if res[0] != 0:
      +            raise j.exceptions.JSException(f"Failed to generate self-signed certificate (using mkcert).{res}")
      +
      +    else:
      +        if j.sals.fs.exists(f"{keypempath}") and j.sals.fs.exists(f"{certpempath}"):
      +            return
      +        res = j.sals.process.execute(
      +            f"openssl req -nodes -x509 -newkey rsa:4096 -keyout {keypempath} -out {certpempath} -days 365 -subj '/CN=localhost'"
      +        )
      +        if res[0] != 0:
      +            raise j.exceptions.JSException(f"Failed to generate self-signed certificate (using openssl).{res}")
      +
      +
      +
      +def get_config(self) +
      +
      +
      +
      + +Expand source code + +
      def get_config(self):
      +    return render_config_template("website", base_dir=j.core.dirs.BASEDIR, website=self)
      +
      +
      +
      +def get_custom_location(self, name) +
      +
      +
      +
      + +Expand source code + +
      def get_custom_location(self, name):
      +    location = self.locations.get(name)
      +    location.location_type = LocationType.CUSTOM
      +    return location
      +
      +
      +
      +def get_locations(self) +
      +
      +
      +
      + +Expand source code + +
      def get_locations(self):
      +    for location in self.locations.list_all():
      +        yield self.locations.get(location)
      +
      +
      +
      +def get_proxy_location(self, name) +
      +
      +
      +
      + +Expand source code + +
      def get_proxy_location(self, name):
      +    location = self.locations.get(name)
      +    location.location_type = LocationType.PROXY
      +    return location
      +
      +
      +
      +def get_static_location(self, name) +
      +
      +
      +
      + +Expand source code + +
      def get_static_location(self, name):
      +    location = self.locations.get(name)
      +    location.location_type = LocationType.STATIC
      +    return location
      +
      +
      +
      +def install_certifcate(self) +
      +
      +

      Construct and Execute install certificate command +Alternative to certbot install

      +
      + +Expand source code + +
      def install_certifcate(self):
      +    """Construct and Execute install certificate command
      +    Alternative to certbot install
      +
      +    """
      +    cmd = self.certbot.install_cmd
      +    j.logger.debug(f"Execute: {' '.join(cmd)}")
      +    rc, out, err = j.sals.process.execute(cmd)
      +    if rc > 0:
      +        j.logger.error(f"Installing certificate failed {out}\n{err}")
      +    else:
      +        j.logger.info(f"Certificate installed successfully {out}")
      +
      +
      +
      +def obtain_and_install_certifcate(self, retries=6) +
      +
      +

      Construct and Execute run certificate command,This will issue a new certificate managed by Certbot +Alternative to certbot run

      +

      Args

      +
      +
      retries : int, optional
      +
      Number of retries Certbot will try to install the certificate if failed. Defaults to 6.
      +
      +
      + +Expand source code + +
      def obtain_and_install_certifcate(self, retries=6):
      +    """Construct and Execute run certificate command,This will issue a new certificate managed by Certbot
      +    Alternative to certbot run
      +
      +    Args:
      +        retries (int, optional): Number of retries Certbot will try to install the certificate if failed. Defaults to 6.
      +    """
      +    cmd = self.certbot.run_cmd
      +    j.logger.debug(f"Execute: {' '.join(cmd)}")
      +    for _ in range(retries):
      +        rc, out, err = j.sals.process.execute(cmd)
      +        if rc > 0:
      +            j.logger.error(f"Generating certificate failed {out}\n{err}")
      +        else:
      +            j.logger.error(f"Certificate Generated successfully {out}")
      +            break
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class ZerosslCertbot +(parent_=None, instance_name_=None, **values) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class ZerosslCertbot(NginxCertbot):
      +    SERVER_URL = "https://acme.zerossl.com/v2/DV90"
      +    KEY_CREDENTIALS_URL = "https://api.zerossl.com/acme/eab-credentials"
      +    EMAIL_CREDENTIALS_URL = "https://api.zerossl.com/acme/eab-credentials-email"
      +
      +    api_key_ = fields.Secret()
      +    server = fields.URL(default=SERVER_URL)
      +
      +    @property
      +    def run_cmd(self):
      +        # get eab_kid and eab_hmac_key based on email or api_key_
      +        if not self.email and not self.api_key_:
      +            raise Input("email or api_key_ must be provided")
      +
      +        # set them to get the full run-cmd with correct arguments
      +        if self.api_key_:
      +            resp = j.tools.http.post(self.KEY_CREDENTIALS_URL, params={"access_key": self.api_key_})
      +        else:
      +            resp = j.tools.http.post(self.EMAIL_CREDENTIALS_URL, data={"email": self.email})
      +
      +        resp.raise_for_status()
      +        data = resp.json()
      +
      +        self.eab_kid = data["eab_kid"]
      +        self.eab_hmac_key = data["eab_hmac_key"]
      +
      +        return super().run_cmd
      +
      +

      Ancestors

      + +

      Class variables

      +
      +
      var EMAIL_CREDENTIALS_URL
      +
      +
      +
      +
      var KEY_CREDENTIALS_URL
      +
      +
      +
      +
      var SERVER_URL
      +
      +
      +
      +
      +

      Instance variables

      +
      +
      var api_key_
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var run_cmd
      +
      +
      +
      + +Expand source code + +
      @property
      +def run_cmd(self):
      +    # get eab_kid and eab_hmac_key based on email or api_key_
      +    if not self.email and not self.api_key_:
      +        raise Input("email or api_key_ must be provided")
      +
      +    # set them to get the full run-cmd with correct arguments
      +    if self.api_key_:
      +        resp = j.tools.http.post(self.KEY_CREDENTIALS_URL, params={"access_key": self.api_key_})
      +    else:
      +        resp = j.tools.http.post(self.EMAIL_CREDENTIALS_URL, data={"email": self.email})
      +
      +    resp.raise_for_status()
      +    data = resp.json()
      +
      +    self.eab_kid = data["eab_kid"]
      +    self.eab_hmac_key = data["eab_hmac_key"]
      +
      +    return super().run_cmd
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/nginx/utils.html b/docs/api/jumpscale/sals/nginx/utils.html new file mode 100644 index 000000000..b72694213 --- /dev/null +++ b/docs/api/jumpscale/sals/nginx/utils.html @@ -0,0 +1,88 @@ + + + + + + +jumpscale.sals.nginx.utils API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.sals.nginx.utils

      +
      +
      +
      + +Expand source code + +
      from jumpscale.loader import j
      +
      +
      +DIR_PATH = j.sals.fs.dirname(j.sals.fs.realpath(__file__))
      +env = j.tools.jinja2.get_env(j.sals.fs.join_paths(DIR_PATH, "templates"))
      +
      +
      +def render_config_template(name, **kwargs):
      +    return env.get_template(f"{name}.conf").render(**kwargs)
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def render_config_template(name, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def render_config_template(name, **kwargs):
      +    return env.get_template(f"{name}.conf").render(**kwargs)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/nginx_proxy/index.html b/docs/api/jumpscale/sals/nginx_proxy/index.html new file mode 100644 index 000000000..f4af40109 --- /dev/null +++ b/docs/api/jumpscale/sals/nginx_proxy/index.html @@ -0,0 +1,924 @@ + + + + + + +jumpscale.sals.nginx_proxy API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.sals.nginx_proxy

      +
      +
      +
      + +Expand source code + +
      HEADER_PREFIX = "proxy_set_header "
      +
      +
      +class NginxReverseProxyConfig:
      +    """
      +        Provides ways to override options and headers for http and https server blocks.
      +        It's intended for usage in a nginx reverse proxy that serves a single domain.
      +        Maybe later we can add more customization to add new server blocks or modify global options.
      +    """
      +
      +    def __init__(self):
      +        self.http_options = {
      +            "proxy_set_header X-Real-IP": "$remote_addr",
      +            "proxy_set_header Host": "$host",
      +            "proxy_set_header X-Forwarded-For": "$proxy_add_x_forwarded_for",
      +            "proxy_set_header X-Forwarded-Proto": "$scheme",
      +            "proxy_connect_timeout": "600",
      +            "proxy_send_timeout": "86400",
      +            "proxy_read_timeout": "86400",
      +            "send_timeout": "600",
      +            "proxy_http_version": "1.1",
      +            "proxy_set_header Upgrade": "$http_upgrade",
      +            "proxy_set_header Connection": '"upgrade"',
      +        }
      +
      +        self.https_options = {
      +            "proxy_set_header X-Real-IP": "$remote_addr",
      +            "proxy_set_header Host": "$host",
      +            "proxy_set_header X-Forwarded-For": "$proxy_add_x_forwarded_for",
      +            "proxy_set_header X-Forwarded-Proto": "$scheme",
      +            "proxy_connect_timeout": "600",
      +            "proxy_send_timeout": "86400",
      +            "proxy_read_timeout": "86400",
      +            "send_timeout": "600",
      +            "proxy_http_version": "1.1",
      +            "proxy_set_header Upgrade": "$http_upgrade",
      +            "proxy_set_header Connection": '"upgrade"',
      +        }
      +
      +    def add_http_option(self, key, *args):
      +        """add an option to http. Override if exists.
      +
      +        Args:
      +            key (str): The name of the option to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        val = " ".join([str(x) for x in args])
      +        self.http_options[key] = val
      +
      +    def add_https_option(self, key, *args):
      +        """add an option to https. Override if exists.
      +
      +        Args:
      +            key (str): The name of the option to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        val = " ".join([str(x) for x in args])
      +        self.https_options[key] = val
      +
      +    def add_option(self, key, *args):
      +        """add an option to http and https. Override if exists.
      +
      +        Args:
      +            key (str): The name of the option to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        self.add_http_option(key, *args)
      +        self.add_https_option(key, *args)
      +
      +    def add_http_header(self, name, *args):
      +        """add a header to http. Override if exists.
      +
      +        Args:
      +            name (str): The name of the header to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        name = str(name)
      +        self.add_http_option(HEADER_PREFIX + name, *args)
      +
      +    def add_https_header(self, name, *args):
      +        """add a header to https. Override if exists.
      +
      +        Args:
      +            name (str): The name of the header to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        name = str(name)
      +        self.add_https_option(HEADER_PREFIX + name, *args)
      +
      +    def add_header(self, name, *args):
      +        """add a header to http and https. Override if exists.
      +
      +        Args:
      +            name (str): The name of the header to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        self.add_http_header(name, *args)
      +        self.add_https_header(name, *args)
      +
      +    def remove_http_option(self, key):
      +        """remove an http option
      +
      +        Args:
      +            key (str): The name of the option to be deleted
      +        """
      +        key = str(key)
      +        self.http_options.pop(key, None)
      +
      +    def remove_https_option(self, key):
      +        """remove an https option
      +
      +        Args:
      +            key (str): The name of the option to be deleted.
      +        """
      +        key = str(key)
      +        self.https_options.pop(key, None)
      +
      +    def remove_option(self, key):
      +        """remove an option from both http and https
      +
      +        Args:
      +            key (str): The name of the option to be deleted.
      +        """
      +        self.remove_http_option(key)
      +        self.remove_https_option(key)
      +
      +    def remove_http_header(self, name):
      +        """remove a specific http header by its name
      +
      +        Args:
      +            header (str): The name of the header to be deleted.
      +        """
      +        name = str(name)
      +        self.remove_http_option(HEADER_PREFIX + name)
      +
      +    def remove_https_header(self, name):
      +        """remove a specific https header by its name
      +
      +        Args:
      +            header (str): The name of the header to be deleted.
      +        """
      +        name = str(name)
      +        self.remove_https_option(HEADER_PREFIX + name)
      +
      +    def remove_header(self, name):
      +        """remove a specific header from http and https
      +
      +        Args:
      +            header (str): The name of the header to be deleted.
      +        """
      +        self.remove_http_header(name)
      +        self.remove_https_header(name)
      +
      +    def clear_http(self):
      +        """clear all http headers"""
      +        self.http_options = {}
      +
      +    def clear_https(self):
      +        """clear all https headers"""
      +        self.https_options = {}
      +
      +    def clear_all(self):
      +        """clear all http and https headers"""
      +        self.clear_http()
      +        self.clear_https()
      +
      +    def serialize_http(self):
      +        """Seriallize the http options
      +
      +        Returns:
      +            A string representing the http options to be added to the nginx http location block
      +        """
      +        res = ""
      +        for k, v in self.http_options.items():
      +            res += f"{k} {v};\n"
      +        return res
      +
      +    def serialize_https(self):
      +        """Seriallize the https options
      +
      +        Returns:
      +            A string representing the http options to be added to the nginx https location block
      +        """
      +        res = ""
      +        for k, v in self.https_options.items():
      +            res += f"{k} {v};\n"
      +        return res
      +
      +    def serialize(self):
      +        """Seriallize the http and https options
      +
      +        Returns:
      +            A pair of strings representing the http/https options to be added to the nginx http/https location block
      +        """
      +        return self.serialize_http(), self.serialize_https()
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class NginxReverseProxyConfig +
      +
      +

      Provides ways to override options and headers for http and https server blocks. +It's intended for usage in a nginx reverse proxy that serves a single domain. +Maybe later we can add more customization to add new server blocks or modify global options.

      +
      + +Expand source code + +
      class NginxReverseProxyConfig:
      +    """
      +        Provides ways to override options and headers for http and https server blocks.
      +        It's intended for usage in a nginx reverse proxy that serves a single domain.
      +        Maybe later we can add more customization to add new server blocks or modify global options.
      +    """
      +
      +    def __init__(self):
      +        self.http_options = {
      +            "proxy_set_header X-Real-IP": "$remote_addr",
      +            "proxy_set_header Host": "$host",
      +            "proxy_set_header X-Forwarded-For": "$proxy_add_x_forwarded_for",
      +            "proxy_set_header X-Forwarded-Proto": "$scheme",
      +            "proxy_connect_timeout": "600",
      +            "proxy_send_timeout": "86400",
      +            "proxy_read_timeout": "86400",
      +            "send_timeout": "600",
      +            "proxy_http_version": "1.1",
      +            "proxy_set_header Upgrade": "$http_upgrade",
      +            "proxy_set_header Connection": '"upgrade"',
      +        }
      +
      +        self.https_options = {
      +            "proxy_set_header X-Real-IP": "$remote_addr",
      +            "proxy_set_header Host": "$host",
      +            "proxy_set_header X-Forwarded-For": "$proxy_add_x_forwarded_for",
      +            "proxy_set_header X-Forwarded-Proto": "$scheme",
      +            "proxy_connect_timeout": "600",
      +            "proxy_send_timeout": "86400",
      +            "proxy_read_timeout": "86400",
      +            "send_timeout": "600",
      +            "proxy_http_version": "1.1",
      +            "proxy_set_header Upgrade": "$http_upgrade",
      +            "proxy_set_header Connection": '"upgrade"',
      +        }
      +
      +    def add_http_option(self, key, *args):
      +        """add an option to http. Override if exists.
      +
      +        Args:
      +            key (str): The name of the option to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        val = " ".join([str(x) for x in args])
      +        self.http_options[key] = val
      +
      +    def add_https_option(self, key, *args):
      +        """add an option to https. Override if exists.
      +
      +        Args:
      +            key (str): The name of the option to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        val = " ".join([str(x) for x in args])
      +        self.https_options[key] = val
      +
      +    def add_option(self, key, *args):
      +        """add an option to http and https. Override if exists.
      +
      +        Args:
      +            key (str): The name of the option to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        self.add_http_option(key, *args)
      +        self.add_https_option(key, *args)
      +
      +    def add_http_header(self, name, *args):
      +        """add a header to http. Override if exists.
      +
      +        Args:
      +            name (str): The name of the header to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        name = str(name)
      +        self.add_http_option(HEADER_PREFIX + name, *args)
      +
      +    def add_https_header(self, name, *args):
      +        """add a header to https. Override if exists.
      +
      +        Args:
      +            name (str): The name of the header to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        name = str(name)
      +        self.add_https_option(HEADER_PREFIX + name, *args)
      +
      +    def add_header(self, name, *args):
      +        """add a header to http and https. Override if exists.
      +
      +        Args:
      +            name (str): The name of the header to be set
      +            args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +        """
      +        self.add_http_header(name, *args)
      +        self.add_https_header(name, *args)
      +
      +    def remove_http_option(self, key):
      +        """remove an http option
      +
      +        Args:
      +            key (str): The name of the option to be deleted
      +        """
      +        key = str(key)
      +        self.http_options.pop(key, None)
      +
      +    def remove_https_option(self, key):
      +        """remove an https option
      +
      +        Args:
      +            key (str): The name of the option to be deleted.
      +        """
      +        key = str(key)
      +        self.https_options.pop(key, None)
      +
      +    def remove_option(self, key):
      +        """remove an option from both http and https
      +
      +        Args:
      +            key (str): The name of the option to be deleted.
      +        """
      +        self.remove_http_option(key)
      +        self.remove_https_option(key)
      +
      +    def remove_http_header(self, name):
      +        """remove a specific http header by its name
      +
      +        Args:
      +            header (str): The name of the header to be deleted.
      +        """
      +        name = str(name)
      +        self.remove_http_option(HEADER_PREFIX + name)
      +
      +    def remove_https_header(self, name):
      +        """remove a specific https header by its name
      +
      +        Args:
      +            header (str): The name of the header to be deleted.
      +        """
      +        name = str(name)
      +        self.remove_https_option(HEADER_PREFIX + name)
      +
      +    def remove_header(self, name):
      +        """remove a specific header from http and https
      +
      +        Args:
      +            header (str): The name of the header to be deleted.
      +        """
      +        self.remove_http_header(name)
      +        self.remove_https_header(name)
      +
      +    def clear_http(self):
      +        """clear all http headers"""
      +        self.http_options = {}
      +
      +    def clear_https(self):
      +        """clear all https headers"""
      +        self.https_options = {}
      +
      +    def clear_all(self):
      +        """clear all http and https headers"""
      +        self.clear_http()
      +        self.clear_https()
      +
      +    def serialize_http(self):
      +        """Seriallize the http options
      +
      +        Returns:
      +            A string representing the http options to be added to the nginx http location block
      +        """
      +        res = ""
      +        for k, v in self.http_options.items():
      +            res += f"{k} {v};\n"
      +        return res
      +
      +    def serialize_https(self):
      +        """Seriallize the https options
      +
      +        Returns:
      +            A string representing the http options to be added to the nginx https location block
      +        """
      +        res = ""
      +        for k, v in self.https_options.items():
      +            res += f"{k} {v};\n"
      +        return res
      +
      +    def serialize(self):
      +        """Seriallize the http and https options
      +
      +        Returns:
      +            A pair of strings representing the http/https options to be added to the nginx http/https location block
      +        """
      +        return self.serialize_http(), self.serialize_https()
      +
      +

      Methods

      +
      +
      +def add_header(self, name, *args) +
      +
      +

      add a header to http and https. Override if exists.

      +

      Args

      +
      +
      name : str
      +
      The name of the header to be set
      +
      args : list
      +
      Joined by spaces after stringifying its entries to get the header value.
      +
      +
      + +Expand source code + +
      def add_header(self, name, *args):
      +    """add a header to http and https. Override if exists.
      +
      +    Args:
      +        name (str): The name of the header to be set
      +        args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +    """
      +    self.add_http_header(name, *args)
      +    self.add_https_header(name, *args)
      +
      +
      +
      +def add_http_header(self, name, *args) +
      +
      +

      add a header to http. Override if exists.

      +

      Args

      +
      +
      name : str
      +
      The name of the header to be set
      +
      args : list
      +
      Joined by spaces after stringifying its entries to get the header value.
      +
      +
      + +Expand source code + +
      def add_http_header(self, name, *args):
      +    """add a header to http. Override if exists.
      +
      +    Args:
      +        name (str): The name of the header to be set
      +        args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +    """
      +    name = str(name)
      +    self.add_http_option(HEADER_PREFIX + name, *args)
      +
      +
      +
      +def add_http_option(self, key, *args) +
      +
      +

      add an option to http. Override if exists.

      +

      Args

      +
      +
      key : str
      +
      The name of the option to be set
      +
      args : list
      +
      Joined by spaces after stringifying its entries to get the header value.
      +
      +
      + +Expand source code + +
      def add_http_option(self, key, *args):
      +    """add an option to http. Override if exists.
      +
      +    Args:
      +        key (str): The name of the option to be set
      +        args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +    """
      +    val = " ".join([str(x) for x in args])
      +    self.http_options[key] = val
      +
      +
      +
      +def add_https_header(self, name, *args) +
      +
      +

      add a header to https. Override if exists.

      +

      Args

      +
      +
      name : str
      +
      The name of the header to be set
      +
      args : list
      +
      Joined by spaces after stringifying its entries to get the header value.
      +
      +
      + +Expand source code + +
      def add_https_header(self, name, *args):
      +    """add a header to https. Override if exists.
      +
      +    Args:
      +        name (str): The name of the header to be set
      +        args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +    """
      +    name = str(name)
      +    self.add_https_option(HEADER_PREFIX + name, *args)
      +
      +
      +
      +def add_https_option(self, key, *args) +
      +
      +

      add an option to https. Override if exists.

      +

      Args

      +
      +
      key : str
      +
      The name of the option to be set
      +
      args : list
      +
      Joined by spaces after stringifying its entries to get the header value.
      +
      +
      + +Expand source code + +
      def add_https_option(self, key, *args):
      +    """add an option to https. Override if exists.
      +
      +    Args:
      +        key (str): The name of the option to be set
      +        args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +    """
      +    val = " ".join([str(x) for x in args])
      +    self.https_options[key] = val
      +
      +
      +
      +def add_option(self, key, *args) +
      +
      +

      add an option to http and https. Override if exists.

      +

      Args

      +
      +
      key : str
      +
      The name of the option to be set
      +
      args : list
      +
      Joined by spaces after stringifying its entries to get the header value.
      +
      +
      + +Expand source code + +
      def add_option(self, key, *args):
      +    """add an option to http and https. Override if exists.
      +
      +    Args:
      +        key (str): The name of the option to be set
      +        args (list): Joined by spaces after stringifying its entries to get the header value.
      +
      +    """
      +    self.add_http_option(key, *args)
      +    self.add_https_option(key, *args)
      +
      +
      +
      +def clear_all(self) +
      +
      +

      clear all http and https headers

      +
      + +Expand source code + +
      def clear_all(self):
      +    """clear all http and https headers"""
      +    self.clear_http()
      +    self.clear_https()
      +
      +
      +
      +def clear_http(self) +
      +
      +

      clear all http headers

      +
      + +Expand source code + +
      def clear_http(self):
      +    """clear all http headers"""
      +    self.http_options = {}
      +
      +
      +
      +def clear_https(self) +
      +
      +

      clear all https headers

      +
      + +Expand source code + +
      def clear_https(self):
      +    """clear all https headers"""
      +    self.https_options = {}
      +
      +
      +
      +def remove_header(self, name) +
      +
      +

      remove a specific header from http and https

      +

      Args

      +
      +
      header : str
      +
      The name of the header to be deleted.
      +
      +
      + +Expand source code + +
      def remove_header(self, name):
      +    """remove a specific header from http and https
      +
      +    Args:
      +        header (str): The name of the header to be deleted.
      +    """
      +    self.remove_http_header(name)
      +    self.remove_https_header(name)
      +
      +
      +
      +def remove_http_header(self, name) +
      +
      +

      remove a specific http header by its name

      +

      Args

      +
      +
      header : str
      +
      The name of the header to be deleted.
      +
      +
      + +Expand source code + +
      def remove_http_header(self, name):
      +    """remove a specific http header by its name
      +
      +    Args:
      +        header (str): The name of the header to be deleted.
      +    """
      +    name = str(name)
      +    self.remove_http_option(HEADER_PREFIX + name)
      +
      +
      +
      +def remove_http_option(self, key) +
      +
      +

      remove an http option

      +

      Args

      +
      +
      key : str
      +
      The name of the option to be deleted
      +
      +
      + +Expand source code + +
      def remove_http_option(self, key):
      +    """remove an http option
      +
      +    Args:
      +        key (str): The name of the option to be deleted
      +    """
      +    key = str(key)
      +    self.http_options.pop(key, None)
      +
      +
      +
      +def remove_https_header(self, name) +
      +
      +

      remove a specific https header by its name

      +

      Args

      +
      +
      header : str
      +
      The name of the header to be deleted.
      +
      +
      + +Expand source code + +
      def remove_https_header(self, name):
      +    """remove a specific https header by its name
      +
      +    Args:
      +        header (str): The name of the header to be deleted.
      +    """
      +    name = str(name)
      +    self.remove_https_option(HEADER_PREFIX + name)
      +
      +
      +
      +def remove_https_option(self, key) +
      +
      +

      remove an https option

      +

      Args

      +
      +
      key : str
      +
      The name of the option to be deleted.
      +
      +
      + +Expand source code + +
      def remove_https_option(self, key):
      +    """remove an https option
      +
      +    Args:
      +        key (str): The name of the option to be deleted.
      +    """
      +    key = str(key)
      +    self.https_options.pop(key, None)
      +
      +
      +
      +def remove_option(self, key) +
      +
      +

      remove an option from both http and https

      +

      Args

      +
      +
      key : str
      +
      The name of the option to be deleted.
      +
      +
      + +Expand source code + +
      def remove_option(self, key):
      +    """remove an option from both http and https
      +
      +    Args:
      +        key (str): The name of the option to be deleted.
      +    """
      +    self.remove_http_option(key)
      +    self.remove_https_option(key)
      +
      +
      +
      +def serialize(self) +
      +
      +

      Seriallize the http and https options

      +

      Returns

      +

      A pair of strings representing the http/https options to be added to the nginx http/https location block

      +
      + +Expand source code + +
      def serialize(self):
      +    """Seriallize the http and https options
      +
      +    Returns:
      +        A pair of strings representing the http/https options to be added to the nginx http/https location block
      +    """
      +    return self.serialize_http(), self.serialize_https()
      +
      +
      +
      +def serialize_http(self) +
      +
      +

      Seriallize the http options

      +

      Returns

      +

      A string representing the http options to be added to the nginx http location block

      +
      + +Expand source code + +
      def serialize_http(self):
      +    """Seriallize the http options
      +
      +    Returns:
      +        A string representing the http options to be added to the nginx http location block
      +    """
      +    res = ""
      +    for k, v in self.http_options.items():
      +        res += f"{k} {v};\n"
      +    return res
      +
      +
      +
      +def serialize_https(self) +
      +
      +

      Seriallize the https options

      +

      Returns

      +

      A string representing the http options to be added to the nginx https location block

      +
      + +Expand source code + +
      def serialize_https(self):
      +    """Seriallize the https options
      +
      +    Returns:
      +        A string representing the http options to be added to the nginx https location block
      +    """
      +    res = ""
      +    for k, v in self.https_options.items():
      +        res += f"{k} {v};\n"
      +    return res
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/process/index.html b/docs/api/jumpscale/sals/process/index.html index a4cbb3330..d2babb6e4 100644 --- a/docs/api/jumpscale/sals/process/index.html +++ b/docs/api/jumpscale/sals/process/index.html @@ -3013,4 +3013,4 @@

      Index

      Generated by pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/testdocs/index.html b/docs/api/jumpscale/sals/testdocs/index.html index ec25dd209..08a363a27 100644 --- a/docs/api/jumpscale/sals/testdocs/index.html +++ b/docs/api/jumpscale/sals/testdocs/index.html @@ -400,4 +400,4 @@

      pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/unix/index.html b/docs/api/jumpscale/sals/unix/index.html index ab45ce610..2e9c38a5f 100644 --- a/docs/api/jumpscale/sals/unix/index.html +++ b/docs/api/jumpscale/sals/unix/index.html @@ -68,4 +68,4 @@

      Index

      Generated by pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/sals/unix/user.html b/docs/api/jumpscale/sals/unix/user.html index e6f37e8ec..029661582 100644 --- a/docs/api/jumpscale/sals/unix/user.html +++ b/docs/api/jumpscale/sals/unix/user.html @@ -203,4 +203,4 @@

      Index

      Generated by pdoc 0.10.0.

      - + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/appserver/index.html b/docs/api/jumpscale/servers/appserver/index.html new file mode 100644 index 000000000..5ea2b2178 --- /dev/null +++ b/docs/api/jumpscale/servers/appserver/index.html @@ -0,0 +1,167 @@ + + + + + + +jumpscale.servers.appserver API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.servers.appserver

      +
      +
      +
      + +Expand source code + +
      from functools import wraps
      +from json import JSONDecodeError
      +from urllib.parse import urlencode, quote, unquote
      +
      +import nacl
      +import requests
      +from beaker.middleware import SessionMiddleware
      +from bottle import Bottle, abort, redirect, request, response
      +from nacl.public import Box
      +from nacl.signing import VerifyKey
      +
      +from jumpscale.loader import j
      +
      +SESSION_OPTS = {"session.type": "file", "session.data_dir": f"{j.core.dirs.VARDIR}/data", "session.auto": True}
      +
      +
      +class StripPathMiddleware(object):
      +    """
      +    a middle ware for bottle apps to strip slashes
      +    """
      +
      +    def __init__(self, app):
      +        self.app = app
      +
      +    def __call__(self, e, h):
      +        e["PATH_INFO"] = e["PATH_INFO"].rstrip("/")
      +        return self.app(e, h)
      +
      +
      +def make_main_app():
      +    app = Bottle()
      +    # mount sub applications on this object
      +    return app
      +
      +
      +def apply_main_middlewares(app):
      +    app = StripPathMiddleware(app)
      +    return SessionMiddleware(app, SESSION_OPTS)
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def apply_main_middlewares(app) +
      +
      +
      +
      + +Expand source code + +
      def apply_main_middlewares(app):
      +    app = StripPathMiddleware(app)
      +    return SessionMiddleware(app, SESSION_OPTS)
      +
      +
      +
      +def make_main_app() +
      +
      +
      +
      + +Expand source code + +
      def make_main_app():
      +    app = Bottle()
      +    # mount sub applications on this object
      +    return app
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class StripPathMiddleware +(app) +
      +
      +

      a middle ware for bottle apps to strip slashes

      +
      + +Expand source code + +
      class StripPathMiddleware(object):
      +    """
      +    a middle ware for bottle apps to strip slashes
      +    """
      +
      +    def __init__(self, app):
      +        self.app = app
      +
      +    def __call__(self, e, h):
      +        e["PATH_INFO"] = e["PATH_INFO"].rstrip("/")
      +        return self.app(e, h)
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/baseactor.html b/docs/api/jumpscale/servers/gedis/baseactor.html new file mode 100644 index 000000000..7c106c481 --- /dev/null +++ b/docs/api/jumpscale/servers/gedis/baseactor.html @@ -0,0 +1,350 @@ + + + + + + +jumpscale.servers.gedis.baseactor API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.servers.gedis.baseactor

      +
      +
      +
      + +Expand source code + +
      import inspect
      +import sys
      +from functools import wraps
      +from jumpscale.loader import j
      +
      +
      +def actor_method(func):
      +    @wraps(func)
      +    def wrapper(*args, **kwargs):
      +        # verify args and kwargs types
      +        signature = inspect.signature(func)
      +        try:
      +            bound = signature.bind(*args, **kwargs)
      +        except TypeError as e:
      +            raise j.exceptions.Value(str(e))
      +
      +        for name, value in bound.arguments.items():
      +            annotation = signature.parameters[name].annotation
      +            if annotation not in (None, inspect._empty) and not isinstance(value, annotation):
      +                raise j.exceptions.Value(
      +                    f"parameter ({name}) supposed to be of type ({annotation.__name__}), but found ({type(value).__name__})"
      +                )
      +
      +        # call method
      +        result = func(*bound.args, **bound.kwargs)
      +        # verify result type
      +        return_type = signature.return_annotation
      +        if return_type is inspect._empty or return_type is None:
      +            return_type = type(None)
      +
      +        if not isinstance(result, return_type):
      +            raise j.exceptions.Value(f"method is supposed to return ({return_type}), but it returned ({type(result)})")
      +
      +        return result
      +
      +    return wrapper
      +
      +
      +class BaseActor:
      +    def __init__(self):
      +        self.path = None
      +
      +    @actor_method
      +    def info(self) -> dict:
      +        info = {}
      +        info["path"] = self.path
      +        info["methods"] = {}
      +
      +        methods = inspect.getmembers(self, predicate=inspect.ismethod)
      +        for name, attr in methods:
      +            if name.startswith("_"):
      +                continue
      +
      +            signature = inspect.signature(attr)
      +            info["methods"][name] = {}
      +            info["methods"][name]["args"] = []
      +            info["methods"][name]["doc"] = attr.__doc__ or ""
      +
      +            for parameter_name, parameter in signature.parameters.items():
      +                info["methods"][name]["args"].append((parameter_name, parameter.annotation.__name__))
      +
      +        return info
      +
      +    def __validate_actor__(self):
      +        def validate_annotation(annotation, annotated):
      +            if annotation is None or annotation is inspect._empty:
      +                return
      +
      +            if not (inspect.isclass(annotation) and annotation.__class__ == type):
      +                raise ValueError("annotation must be a class type")
      +
      +            if annotation not in (str, int, float, list, tuple, dict, bool):
      +                if annotation.__module__ == "builtins":
      +                    raise ValueError(f"unsupported type ({annotation.__name__})")
      +
      +                for method in ["to_dict", "from_dict"]:
      +                    if method not in dir(annotation):
      +                        raise ValueError(
      +                            f"type ({annotation.__name__}) which annotate {annotated} doesn't have {method} method"
      +                        )
      +
      +        result = {"valid": True, "errors": {}}
      +        methods = inspect.getmembers(self, predicate=inspect.ismethod)
      +        for method_name, method_callable in methods:
      +            if method_name.startswith("_"):
      +                continue
      +
      +            result["errors"][method_name] = []
      +            signature = inspect.signature(method_callable)
      +            try:
      +                validate_annotation(signature.return_annotation, "return")
      +            except ValueError as e:
      +                result["errors"][method_name].append(str(e))
      +
      +            for name, parameter in signature.parameters.items():
      +                try:
      +                    validate_annotation(parameter.annotation, f"parameter ({name})")
      +                except ValueError as e:
      +                    result["errors"][method_name].append(str(e))
      +
      +        if any(result["errors"].values()):
      +            result["valid"] = False
      +
      +        return result
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def actor_method(func) +
      +
      +
      +
      + +Expand source code + +
      def actor_method(func):
      +    @wraps(func)
      +    def wrapper(*args, **kwargs):
      +        # verify args and kwargs types
      +        signature = inspect.signature(func)
      +        try:
      +            bound = signature.bind(*args, **kwargs)
      +        except TypeError as e:
      +            raise j.exceptions.Value(str(e))
      +
      +        for name, value in bound.arguments.items():
      +            annotation = signature.parameters[name].annotation
      +            if annotation not in (None, inspect._empty) and not isinstance(value, annotation):
      +                raise j.exceptions.Value(
      +                    f"parameter ({name}) supposed to be of type ({annotation.__name__}), but found ({type(value).__name__})"
      +                )
      +
      +        # call method
      +        result = func(*bound.args, **bound.kwargs)
      +        # verify result type
      +        return_type = signature.return_annotation
      +        if return_type is inspect._empty or return_type is None:
      +            return_type = type(None)
      +
      +        if not isinstance(result, return_type):
      +            raise j.exceptions.Value(f"method is supposed to return ({return_type}), but it returned ({type(result)})")
      +
      +        return result
      +
      +    return wrapper
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class BaseActor +
      +
      +
      +
      + +Expand source code + +
      class BaseActor:
      +    def __init__(self):
      +        self.path = None
      +
      +    @actor_method
      +    def info(self) -> dict:
      +        info = {}
      +        info["path"] = self.path
      +        info["methods"] = {}
      +
      +        methods = inspect.getmembers(self, predicate=inspect.ismethod)
      +        for name, attr in methods:
      +            if name.startswith("_"):
      +                continue
      +
      +            signature = inspect.signature(attr)
      +            info["methods"][name] = {}
      +            info["methods"][name]["args"] = []
      +            info["methods"][name]["doc"] = attr.__doc__ or ""
      +
      +            for parameter_name, parameter in signature.parameters.items():
      +                info["methods"][name]["args"].append((parameter_name, parameter.annotation.__name__))
      +
      +        return info
      +
      +    def __validate_actor__(self):
      +        def validate_annotation(annotation, annotated):
      +            if annotation is None or annotation is inspect._empty:
      +                return
      +
      +            if not (inspect.isclass(annotation) and annotation.__class__ == type):
      +                raise ValueError("annotation must be a class type")
      +
      +            if annotation not in (str, int, float, list, tuple, dict, bool):
      +                if annotation.__module__ == "builtins":
      +                    raise ValueError(f"unsupported type ({annotation.__name__})")
      +
      +                for method in ["to_dict", "from_dict"]:
      +                    if method not in dir(annotation):
      +                        raise ValueError(
      +                            f"type ({annotation.__name__}) which annotate {annotated} doesn't have {method} method"
      +                        )
      +
      +        result = {"valid": True, "errors": {}}
      +        methods = inspect.getmembers(self, predicate=inspect.ismethod)
      +        for method_name, method_callable in methods:
      +            if method_name.startswith("_"):
      +                continue
      +
      +            result["errors"][method_name] = []
      +            signature = inspect.signature(method_callable)
      +            try:
      +                validate_annotation(signature.return_annotation, "return")
      +            except ValueError as e:
      +                result["errors"][method_name].append(str(e))
      +
      +            for name, parameter in signature.parameters.items():
      +                try:
      +                    validate_annotation(parameter.annotation, f"parameter ({name})")
      +                except ValueError as e:
      +                    result["errors"][method_name].append(str(e))
      +
      +        if any(result["errors"].values()):
      +            result["valid"] = False
      +
      +        return result
      +
      +

      Subclasses

      + +

      Methods

      +
      +
      +def info(self) ‑> dict +
      +
      +
      +
      + +Expand source code + +
      @actor_method
      +def info(self) -> dict:
      +    info = {}
      +    info["path"] = self.path
      +    info["methods"] = {}
      +
      +    methods = inspect.getmembers(self, predicate=inspect.ismethod)
      +    for name, attr in methods:
      +        if name.startswith("_"):
      +            continue
      +
      +        signature = inspect.signature(attr)
      +        info["methods"][name] = {}
      +        info["methods"][name]["args"] = []
      +        info["methods"][name]["doc"] = attr.__doc__ or ""
      +
      +        for parameter_name, parameter in signature.parameters.items():
      +            info["methods"][name]["args"].append((parameter_name, parameter.annotation.__name__))
      +
      +    return info
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/example_actor.html b/docs/api/jumpscale/servers/gedis/example_actor.html new file mode 100644 index 000000000..172d9874e --- /dev/null +++ b/docs/api/jumpscale/servers/gedis/example_actor.html @@ -0,0 +1,481 @@ + + + + + + +jumpscale.servers.gedis.example_actor API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.servers.gedis.example_actor

      +
      +
      +
      + +Expand source code + +
      from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
      +from typing import Sequence
      +from jumpscale.loader import j
      +import inspect, sys
      +
      +
      +class TestObject:
      +    def __init__(self):
      +        self.attr = None
      +
      +    def to_dict(self):
      +        return self.__dict__
      +
      +    def from_dict(self, ddict):
      +        self.__dict__ = ddict
      +
      +
      +class Example(BaseActor):
      +    @actor_method
      +    def add_two_ints(self, x: int, y: int) -> int:
      +        """Adds two ints
      +
      +        Arguments:
      +            x {int} -- first int
      +            y {int} -- second int
      +
      +        Returns:
      +            int -- the sum of the two ints
      +        """
      +        return x + y
      +
      +    @actor_method
      +    def concate_two_strings(self, x: str, y: str) -> str:
      +        """Concate two strings
      +
      +        Arguments:
      +            x {str} -- first string
      +            y {str} -- second string
      +
      +        Returns:
      +            str -- the concate of the two strings
      +        """
      +        return x + y
      +
      +    @actor_method
      +    def modify_object(self, myobj: list, new_value: int) -> list:
      +        """Modify atrribute attr of the given object
      +
      +        Arguments:
      +            myobj {TestObject} -- the object to be modified
      +
      +        Returns:
      +            TestObject -- modified object
      +        """
      +        for i in range(len(myobj)):
      +            myobj[i].attr = new_value * (i + 1)
      +        return myobj
      +
      +
      +Actor = Example
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class Example +
      +
      +
      +
      + +Expand source code + +
      class Example(BaseActor):
      +    @actor_method
      +    def add_two_ints(self, x: int, y: int) -> int:
      +        """Adds two ints
      +
      +        Arguments:
      +            x {int} -- first int
      +            y {int} -- second int
      +
      +        Returns:
      +            int -- the sum of the two ints
      +        """
      +        return x + y
      +
      +    @actor_method
      +    def concate_two_strings(self, x: str, y: str) -> str:
      +        """Concate two strings
      +
      +        Arguments:
      +            x {str} -- first string
      +            y {str} -- second string
      +
      +        Returns:
      +            str -- the concate of the two strings
      +        """
      +        return x + y
      +
      +    @actor_method
      +    def modify_object(self, myobj: list, new_value: int) -> list:
      +        """Modify atrribute attr of the given object
      +
      +        Arguments:
      +            myobj {TestObject} -- the object to be modified
      +
      +        Returns:
      +            TestObject -- modified object
      +        """
      +        for i in range(len(myobj)):
      +            myobj[i].attr = new_value * (i + 1)
      +        return myobj
      +
      +

      Ancestors

      + +

      Methods

      +
      +
      +def add_two_ints(self, x: int, y: int) ‑> int +
      +
      +

      Adds two ints

      +

      Arguments

      +

      x {int} – first int +y {int} – second int

      +

      Returns

      +

      int – the sum of the two ints

      +
      + +Expand source code + +
      @actor_method
      +def add_two_ints(self, x: int, y: int) -> int:
      +    """Adds two ints
      +
      +    Arguments:
      +        x {int} -- first int
      +        y {int} -- second int
      +
      +    Returns:
      +        int -- the sum of the two ints
      +    """
      +    return x + y
      +
      +
      +
      +def concate_two_strings(self, x: str, y: str) ‑> str +
      +
      +

      Concate two strings

      +

      Arguments

      +

      x {str} – first string +y {str} – second string

      +

      Returns

      +

      str – the concate of the two strings

      +
      + +Expand source code + +
      @actor_method
      +def concate_two_strings(self, x: str, y: str) -> str:
      +    """Concate two strings
      +
      +    Arguments:
      +        x {str} -- first string
      +        y {str} -- second string
      +
      +    Returns:
      +        str -- the concate of the two strings
      +    """
      +    return x + y
      +
      +
      +
      +def modify_object(self, myobj: list, new_value: int) ‑> list +
      +
      +

      Modify atrribute attr of the given object

      +

      Arguments

      +

      myobj {TestObject} – the object to be modified

      +

      Returns

      +

      TestObject – modified object

      +
      + +Expand source code + +
      @actor_method
      +def modify_object(self, myobj: list, new_value: int) -> list:
      +    """Modify atrribute attr of the given object
      +
      +    Arguments:
      +        myobj {TestObject} -- the object to be modified
      +
      +    Returns:
      +        TestObject -- modified object
      +    """
      +    for i in range(len(myobj)):
      +        myobj[i].attr = new_value * (i + 1)
      +    return myobj
      +
      +
      +
      +
      +
      +class Actor +
      +
      +
      +
      + +Expand source code + +
      class Example(BaseActor):
      +    @actor_method
      +    def add_two_ints(self, x: int, y: int) -> int:
      +        """Adds two ints
      +
      +        Arguments:
      +            x {int} -- first int
      +            y {int} -- second int
      +
      +        Returns:
      +            int -- the sum of the two ints
      +        """
      +        return x + y
      +
      +    @actor_method
      +    def concate_two_strings(self, x: str, y: str) -> str:
      +        """Concate two strings
      +
      +        Arguments:
      +            x {str} -- first string
      +            y {str} -- second string
      +
      +        Returns:
      +            str -- the concate of the two strings
      +        """
      +        return x + y
      +
      +    @actor_method
      +    def modify_object(self, myobj: list, new_value: int) -> list:
      +        """Modify atrribute attr of the given object
      +
      +        Arguments:
      +            myobj {TestObject} -- the object to be modified
      +
      +        Returns:
      +            TestObject -- modified object
      +        """
      +        for i in range(len(myobj)):
      +            myobj[i].attr = new_value * (i + 1)
      +        return myobj
      +
      +

      Ancestors

      + +

      Methods

      +
      +
      +def add_two_ints(self, x: int, y: int) ‑> int +
      +
      +

      Adds two ints

      +

      Arguments

      +

      x {int} – first int +y {int} – second int

      +

      Returns

      +

      int – the sum of the two ints

      +
      + +Expand source code + +
      @actor_method
      +def add_two_ints(self, x: int, y: int) -> int:
      +    """Adds two ints
      +
      +    Arguments:
      +        x {int} -- first int
      +        y {int} -- second int
      +
      +    Returns:
      +        int -- the sum of the two ints
      +    """
      +    return x + y
      +
      +
      +
      +def concate_two_strings(self, x: str, y: str) ‑> str +
      +
      +

      Concate two strings

      +

      Arguments

      +

      x {str} – first string +y {str} – second string

      +

      Returns

      +

      str – the concate of the two strings

      +
      + +Expand source code + +
      @actor_method
      +def concate_two_strings(self, x: str, y: str) -> str:
      +    """Concate two strings
      +
      +    Arguments:
      +        x {str} -- first string
      +        y {str} -- second string
      +
      +    Returns:
      +        str -- the concate of the two strings
      +    """
      +    return x + y
      +
      +
      +
      +def modify_object(self, myobj: list, new_value: int) ‑> list +
      +
      +

      Modify atrribute attr of the given object

      +

      Arguments

      +

      myobj {TestObject} – the object to be modified

      +

      Returns

      +

      TestObject – modified object

      +
      + +Expand source code + +
      @actor_method
      +def modify_object(self, myobj: list, new_value: int) -> list:
      +    """Modify atrribute attr of the given object
      +
      +    Arguments:
      +        myobj {TestObject} -- the object to be modified
      +
      +    Returns:
      +        TestObject -- modified object
      +    """
      +    for i in range(len(myobj)):
      +        myobj[i].attr = new_value * (i + 1)
      +    return myobj
      +
      +
      +
      +
      +
      +class TestObject +
      +
      +
      +
      + +Expand source code + +
      class TestObject:
      +    def __init__(self):
      +        self.attr = None
      +
      +    def to_dict(self):
      +        return self.__dict__
      +
      +    def from_dict(self, ddict):
      +        self.__dict__ = ddict
      +
      +

      Methods

      +
      +
      +def from_dict(self, ddict) +
      +
      +
      +
      + +Expand source code + +
      def from_dict(self, ddict):
      +    self.__dict__ = ddict
      +
      +
      +
      +def to_dict(self) +
      +
      +
      +
      + +Expand source code + +
      def to_dict(self):
      +    return self.__dict__
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/index.html b/docs/api/jumpscale/servers/gedis/index.html new file mode 100644 index 000000000..8b229a253 --- /dev/null +++ b/docs/api/jumpscale/servers/gedis/index.html @@ -0,0 +1,214 @@ + + + + + + +jumpscale.servers.gedis API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.servers.gedis

      +
      +
      +

      Gedis server package provides all code needed to have an RPC server using redis protocol for messaging

      +

      Create Gedis instance

      +
      t = j.servers.gedis.get("test")
      +
      +

      this will create Gedis instance with name test running on +default port 16000

      +
      t = j.servers.gedis.new("test",port=1500)
      +
      +

      This will create Gedis instance with name test running on port 1500

      +

      Start Gedis server

      +
      t.start()
      +
      +

      Stop Gedis server

      +
      t.stop()
      +
      +

      ~> +redis-cli -p 16000 greeter hi +actor greeter isn't loaded +~> +redis-cli -p 16000 system register_actor greeter /home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py +(integer) -1 +~> +redis-cli -p 16000 greeter hi +hello world +~> +redis-cli -p 16000 greeter add2 jo deboeck +"jodeboeck" +~> +fuser -k 16000/tcp

      +

      16000/tcp: +29331 +~> +redis-cli -p 16000 greeter hi +actor greeter isn't loaded +~> +redis-cli -p 16000 system register_actor greeter /home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py +(integer) -1 +~> +redis-cli -p 16000 greeter hi +hello world +~> +redis-cli -p 16000 greeter ping

      +

      pong no? +~> +redis-cli -p 16000 greeter add2 reem khamis +"reemkhamis" +```

      +
      + +Expand source code + +
      """Gedis server package provides all code needed to have an RPC server using redis protocol for messaging
      +
      +### Create Gedis instance
      +```
      +t = j.servers.gedis.get("test")
      +```
      +this will create Gedis instance with name test running on  default port 16000
      +```
      +t = j.servers.gedis.new("test",port=1500)
      +```
      +This will create Gedis instance with name test running on port 1500
      +### Start Gedis server
      +```
      +t.start()
      +```
      +### Stop Gedis server
      +```
      +t.stop()
      +```
      +
      +~>  redis-cli -p 16000 greeter hi
      +actor greeter isn't loaded
      + ~>  redis-cli -p 16000 system register_actor greeter /home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py
      +(integer) -1
      + ~>  redis-cli -p 16000 greeter hi
      + hello world
      + ~>  redis-cli -p 16000 greeter add2 jo deboeck
      + "jodeboeck"
      + ~>  fuser -k 16000/tcp
      +
      +16000/tcp:           29331
      + ~>  redis-cli -p 16000 greeter hi
      + actor greeter isn't loaded
      + ~>  redis-cli -p 16000 system register_actor greeter /home/ahmed/wspace/threefoldtech/js-ng/jumpscale/servers/gedis/example_greeter.py
      +(integer) -1
      + ~>  redis-cli -p 16000 greeter hi
      + hello world
      + ~>  redis-cli -p 16000 greeter ping
      +
      +pong no?
      + ~>  redis-cli -p 16000 greeter add2 reem khamis
      +"reemkhamis"
      +```
      +"""
      +from jumpscale.core.base import StoredFactory
      +
      +
      +def export_module_as():
      +    from .server import GedisServer
      +    from jumpscale.loader import j
      +
      +    j.logger.register("gedis")
      +    return StoredFactory(GedisServer)
      +
      +
      +
      +

      Sub-modules

      +
      +
      jumpscale.servers.gedis.baseactor
      +
      +
      +
      +
      jumpscale.servers.gedis.example_actor
      +
      +
      +
      +
      jumpscale.servers.gedis.server
      +
      +
      +
      +
      jumpscale.servers.gedis.systemactor
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    from .server import GedisServer
      +    from jumpscale.loader import j
      +
      +    j.logger.register("gedis")
      +    return StoredFactory(GedisServer)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/server.html b/docs/api/jumpscale/servers/gedis/server.html new file mode 100644 index 000000000..62f7cffdd --- /dev/null +++ b/docs/api/jumpscale/servers/gedis/server.html @@ -0,0 +1,1091 @@ + + + + + + +jumpscale.servers.gedis.server API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.servers.gedis.server

      +
      +
      +
      + +Expand source code + +
      import inspect
      +import json
      +import sys
      +import os
      +from redis import Redis
      +from enum import Enum
      +from functools import partial
      +from io import BytesIO
      +from signal import SIGKILL, SIGTERM
      +import json
      +import gevent
      +from gevent.pool import Pool
      +from gevent import time
      +from gevent.server import StreamServer
      +from jumpscale.core.base import Base, fields
      +from jumpscale.loader import j
      +from redis.connection import DefaultParser, Encoder
      +from redis.exceptions import ConnectionError, TimeoutError
      +from .baseactor import BaseActor
      +from .systemactor import CoreActor, SystemActor
      +
      +
      +def serialize(obj):
      +    if not isinstance(obj, (str, int, float, list, tuple, dict, bool)):
      +        module = inspect.getmodule(obj).__file__[:-3]
      +        return dict(__serialized__=True, module=module, type=obj.__class__.__name__, data=obj.to_dict())
      +    return obj
      +
      +
      +def deserialize(obj):
      +    if isinstance(obj, dict) and obj.get("__serialized__"):
      +        module = sys.modules[obj["module"]]
      +        object_instance = getattr(module, obj["type"])()
      +        object_instance.from_dict(obj["data"])
      +        return object_instance
      +    return obj
      +
      +
      +class GedisErrorTypes(Enum):
      +    NOT_FOUND = 0
      +    BAD_REQUEST = 1
      +    ACTOR_ERROR = 3
      +    INTERNAL_SERVER_ERROR = 4
      +    PERMISSION_ERROR = 5
      +
      +
      +EXCEPTIONS_MAP = {
      +    j.exceptions.Value: GedisErrorTypes.BAD_REQUEST.value,
      +    j.exceptions.NotFound: GedisErrorTypes.NOT_FOUND.value,
      +    j.exceptions.Permission: GedisErrorTypes.PERMISSION_ERROR.value,
      +}
      +
      +
      +class RedisConnectionAdapter:
      +    def __init__(self, sock):
      +        self.socket = sock
      +        self._sock = sock
      +        self.socket_timeout = 600
      +        self.socket_connect_timeout = 600
      +        self.socket_keepalive = True
      +        self.retry_on_timeout = True
      +        self.socket_keepalive_options = {}
      +        self.encoder = Encoder("utf", "strict", False)
      +
      +
      +class ResponseEncoder:
      +    def __init__(self, socket):
      +        self.socket = socket
      +        self.buffer = BytesIO()
      +
      +    def encode(self, value):
      +        """Respond with data."""
      +        if value is None:
      +            self._write_buffer("$-1\r\n")
      +        elif isinstance(value, int):
      +            self._write_buffer(":{}\r\n".format(value))
      +        elif isinstance(value, bool):
      +            self._write_buffer(":{}\r\n".format(1 if value else 0))
      +        elif isinstance(value, str):
      +            if "\n" in value:
      +                self._bulk(value)
      +            else:
      +                self._write_buffer("+{}\r\n".format(value))
      +        elif isinstance(value, bytes):
      +            self._bulkbytes(value)
      +        elif isinstance(value, list):
      +            if value and value[0] == "*REDIS*":
      +                value = value[1:]
      +            self._array(value)
      +        elif hasattr(value, "__repr__"):
      +            self._bulk(value.__repr__())
      +        else:
      +            value = j.data.serializers.json.dumps(value, encoding="utf-8")
      +            self.encode(value)
      +
      +        self._send()
      +
      +    def status(self, msg="OK"):
      +        """Send a status."""
      +        self._write_buffer("+{}\r\n".format(msg))
      +        self._send()
      +
      +    def error(self, msg):
      +        """Send an error."""
      +        # print("###:%s" % msg)
      +        self._write_buffer("-ERR {}\r\n".format(msg))
      +        self._send()
      +
      +    def _bulk(self, value):
      +        """Send part of a multiline reply."""
      +        data = ["$", str(len(value)), "\r\n", value, "\r\n"]
      +        self._write_buffer("".join(data))
      +
      +    def _array(self, value):
      +        """send an array."""
      +        self._write_buffer("*{}\r\n".format(len(value)))
      +        for item in value:
      +            self.encode(item)
      +
      +    def _bulkbytes(self, value):
      +        data = [b"$", str(len(value)).encode(), b"\r\n", value, b"\r\n"]
      +        self._write_buffer(b"".join(data))
      +
      +    def _write_buffer(self, data):
      +        if isinstance(data, str):
      +            data = data.encode()
      +
      +        self.buffer.write(data)
      +
      +    def _send(self):
      +        self.socket.sendall(self.buffer.getvalue())
      +        self.buffer = BytesIO()  # seems faster then truncating
      +
      +
      +SERIALIZABLE_TYPES = (str, int, float, list, tuple, dict, bool)
      +RESERVED_ACTOR_NAMES = ("core", "system")
      +
      +
      +class GedisServer(Base):
      +    host = fields.String(default="127.0.0.1")
      +    port = fields.Integer(default=16000)
      +    enable_system_actor = fields.Boolean(default=True)
      +    run_async = fields.Boolean(default=True)
      +    _actors = fields.Typed(dict, default={})
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._core_actor = CoreActor()
      +        self._system_actor = SystemActor()
      +        self._loaded_actors = {"core": self._core_actor}
      +
      +    @property
      +    def actors(self):
      +        """Lists saved actors
      +
      +        Returns:
      +            list -- List of saved actors
      +        """
      +        return self._actors
      +
      +    def actor_add(self, actor_name: str, actor_path: str):
      +        """Adds an actor to the server
      +
      +        Arguments:
      +            actor_name {str} -- Actor name
      +            actor_path {str} -- Actor absolute path
      +
      +        Raises:
      +            j.exceptions.Value: raises if actor name is matched one of the reserved actor names
      +            j.exceptions.Value: raises if actor name is not a valid identifier
      +        """
      +        if actor_name in RESERVED_ACTOR_NAMES:
      +            raise j.exceptions.Value("Invalid actor name")
      +
      +        if not actor_name.isidentifier():
      +            raise j.exceptions.Value(f"Actor name should be a valid identifier")
      +
      +        self._actors[actor_name] = actor_path
      +
      +    def actor_delete(self, actor_name: str):
      +        """Removes an actor from the server
      +
      +        Arguments:
      +            actor_name {str} -- Actor name
      +        """
      +        self._actors.pop(actor_name, None)
      +
      +    def start(self):
      +        """Starts the server
      +        """
      +        # register system actor if enabled
      +        if self.enable_system_actor:
      +            self._register_actor("system", self._system_actor)
      +
      +        self._core_actor.set_server(self)
      +        self._system_actor.set_server(self)
      +
      +        # register saved actors
      +        for actor_name, actor_path in self._actors.items():
      +            self._system_actor.register_actor(actor_name, actor_path)
      +
      +        # start the server
      +        self._server = StreamServer((self.host, self.port), self._on_connection, spawn=Pool())
      +        self._server.reuse_addr = True
      +        self._server.start()
      +
      +        j.logger.info(f"Gedis server is started at {self.host}:{self.port}...")
      +
      +    def stop(self):
      +        """Stops the server
      +        """
      +        j.logger.info("Shutting down...")
      +        self._server.stop()
      +
      +    def _register_actor(self, actor_name: str, actor_module: BaseActor):
      +        self._loaded_actors[actor_name] = actor_module
      +
      +    def _unregister_actor(self, actor_name: str):
      +        self._loaded_actors.pop(actor_name, None)
      +
      +    def _execute(self, method, args, kwargs):
      +        response = {}
      +        try:
      +            response["result"] = method(*args, **kwargs)
      +
      +        except Exception as e:
      +            j.logger.exception(f"error while executing {method}", exception=e)
      +
      +            response["error"] = str(e)
      +            response["error_type"] = EXCEPTIONS_MAP.get(e.__class__, GedisErrorTypes.ACTOR_ERROR.value)
      +
      +        return response
      +
      +    def _on_connection(self, socket, address):
      +        j.logger.debug(f"New connection from {address}")
      +        parser = DefaultParser(65536)
      +        connection = RedisConnectionAdapter(socket)
      +        try:
      +            encoder = ResponseEncoder(socket)
      +            parser.on_connect(connection)
      +
      +            while True:
      +                response = dict(success=True, result=None, error=None, error_type=None, is_async=False, task_id=None)
      +                try:
      +                    request = parser.read_response()
      +
      +                    if len(request) < 2:
      +                        response["error"] = "invalid request"
      +                        response["error_type"] = GedisErrorTypes.BAD_REQUEST.value
      +
      +                    else:
      +                        actor_name = request.pop(0).decode()
      +                        method_name = request.pop(0).decode()
      +                        actor_object = self._loaded_actors.get(actor_name)
      +
      +                        if not actor_object:
      +                            response["error"] = "actor not found"
      +                            response["error_type"] = GedisErrorTypes.NOT_FOUND.value
      +
      +                        elif not hasattr(actor_object, method_name):
      +                            response["error"] = "method not found"
      +                            response["error_type"] = GedisErrorTypes.NOT_FOUND.value
      +
      +                        else:
      +                            j.logger.debug(
      +                                f"Executing method {method_name} from actor {actor_name} to client {address}"
      +                            )
      +
      +                            if request:
      +                                args, kwargs = json.loads(request.pop(0), object_hook=deserialize)
      +                            else:
      +                                args, kwargs = (), {}
      +
      +                            method = getattr(actor_object, method_name)
      +                            result = self._execute(method, args, kwargs)
      +                            response.update(result)
      +
      +                except (TimeoutError, ConnectionError):
      +                    j.logger.debug(f"Client {address} closed the connection/or timeout", address)
      +                    parser.on_disconnect()
      +                    return
      +
      +                except Exception as exception:
      +                    j.logger.exception("internal error", exception=exception)
      +                    response["error"] = "internal server error"
      +                    response["error_type"] = GedisErrorTypes.INTERNAL_SERVER_ERROR.value
      +
      +                response["success"] = response["error"] is None
      +                encoder.encode(json.dumps(response, default=serialize))
      +
      +        except BrokenPipeError:
      +            pass
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def deserialize(obj) +
      +
      +
      +
      + +Expand source code + +
      def deserialize(obj):
      +    if isinstance(obj, dict) and obj.get("__serialized__"):
      +        module = sys.modules[obj["module"]]
      +        object_instance = getattr(module, obj["type"])()
      +        object_instance.from_dict(obj["data"])
      +        return object_instance
      +    return obj
      +
      +
      +
      +def serialize(obj) +
      +
      +
      +
      + +Expand source code + +
      def serialize(obj):
      +    if not isinstance(obj, (str, int, float, list, tuple, dict, bool)):
      +        module = inspect.getmodule(obj).__file__[:-3]
      +        return dict(__serialized__=True, module=module, type=obj.__class__.__name__, data=obj.to_dict())
      +    return obj
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class GedisErrorTypes +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
      +
      +

      An enumeration.

      +
      + +Expand source code + +
      class GedisErrorTypes(Enum):
      +    NOT_FOUND = 0
      +    BAD_REQUEST = 1
      +    ACTOR_ERROR = 3
      +    INTERNAL_SERVER_ERROR = 4
      +    PERMISSION_ERROR = 5
      +
      +

      Ancestors

      +
        +
      • enum.Enum
      • +
      +

      Class variables

      +
      +
      var ACTOR_ERROR
      +
      +
      +
      +
      var BAD_REQUEST
      +
      +
      +
      +
      var INTERNAL_SERVER_ERROR
      +
      +
      +
      +
      var NOT_FOUND
      +
      +
      +
      +
      var PERMISSION_ERROR
      +
      +
      +
      +
      +
      +
      +class GedisServer +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class GedisServer(Base):
      +    host = fields.String(default="127.0.0.1")
      +    port = fields.Integer(default=16000)
      +    enable_system_actor = fields.Boolean(default=True)
      +    run_async = fields.Boolean(default=True)
      +    _actors = fields.Typed(dict, default={})
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._core_actor = CoreActor()
      +        self._system_actor = SystemActor()
      +        self._loaded_actors = {"core": self._core_actor}
      +
      +    @property
      +    def actors(self):
      +        """Lists saved actors
      +
      +        Returns:
      +            list -- List of saved actors
      +        """
      +        return self._actors
      +
      +    def actor_add(self, actor_name: str, actor_path: str):
      +        """Adds an actor to the server
      +
      +        Arguments:
      +            actor_name {str} -- Actor name
      +            actor_path {str} -- Actor absolute path
      +
      +        Raises:
      +            j.exceptions.Value: raises if actor name is matched one of the reserved actor names
      +            j.exceptions.Value: raises if actor name is not a valid identifier
      +        """
      +        if actor_name in RESERVED_ACTOR_NAMES:
      +            raise j.exceptions.Value("Invalid actor name")
      +
      +        if not actor_name.isidentifier():
      +            raise j.exceptions.Value(f"Actor name should be a valid identifier")
      +
      +        self._actors[actor_name] = actor_path
      +
      +    def actor_delete(self, actor_name: str):
      +        """Removes an actor from the server
      +
      +        Arguments:
      +            actor_name {str} -- Actor name
      +        """
      +        self._actors.pop(actor_name, None)
      +
      +    def start(self):
      +        """Starts the server
      +        """
      +        # register system actor if enabled
      +        if self.enable_system_actor:
      +            self._register_actor("system", self._system_actor)
      +
      +        self._core_actor.set_server(self)
      +        self._system_actor.set_server(self)
      +
      +        # register saved actors
      +        for actor_name, actor_path in self._actors.items():
      +            self._system_actor.register_actor(actor_name, actor_path)
      +
      +        # start the server
      +        self._server = StreamServer((self.host, self.port), self._on_connection, spawn=Pool())
      +        self._server.reuse_addr = True
      +        self._server.start()
      +
      +        j.logger.info(f"Gedis server is started at {self.host}:{self.port}...")
      +
      +    def stop(self):
      +        """Stops the server
      +        """
      +        j.logger.info("Shutting down...")
      +        self._server.stop()
      +
      +    def _register_actor(self, actor_name: str, actor_module: BaseActor):
      +        self._loaded_actors[actor_name] = actor_module
      +
      +    def _unregister_actor(self, actor_name: str):
      +        self._loaded_actors.pop(actor_name, None)
      +
      +    def _execute(self, method, args, kwargs):
      +        response = {}
      +        try:
      +            response["result"] = method(*args, **kwargs)
      +
      +        except Exception as e:
      +            j.logger.exception(f"error while executing {method}", exception=e)
      +
      +            response["error"] = str(e)
      +            response["error_type"] = EXCEPTIONS_MAP.get(e.__class__, GedisErrorTypes.ACTOR_ERROR.value)
      +
      +        return response
      +
      +    def _on_connection(self, socket, address):
      +        j.logger.debug(f"New connection from {address}")
      +        parser = DefaultParser(65536)
      +        connection = RedisConnectionAdapter(socket)
      +        try:
      +            encoder = ResponseEncoder(socket)
      +            parser.on_connect(connection)
      +
      +            while True:
      +                response = dict(success=True, result=None, error=None, error_type=None, is_async=False, task_id=None)
      +                try:
      +                    request = parser.read_response()
      +
      +                    if len(request) < 2:
      +                        response["error"] = "invalid request"
      +                        response["error_type"] = GedisErrorTypes.BAD_REQUEST.value
      +
      +                    else:
      +                        actor_name = request.pop(0).decode()
      +                        method_name = request.pop(0).decode()
      +                        actor_object = self._loaded_actors.get(actor_name)
      +
      +                        if not actor_object:
      +                            response["error"] = "actor not found"
      +                            response["error_type"] = GedisErrorTypes.NOT_FOUND.value
      +
      +                        elif not hasattr(actor_object, method_name):
      +                            response["error"] = "method not found"
      +                            response["error_type"] = GedisErrorTypes.NOT_FOUND.value
      +
      +                        else:
      +                            j.logger.debug(
      +                                f"Executing method {method_name} from actor {actor_name} to client {address}"
      +                            )
      +
      +                            if request:
      +                                args, kwargs = json.loads(request.pop(0), object_hook=deserialize)
      +                            else:
      +                                args, kwargs = (), {}
      +
      +                            method = getattr(actor_object, method_name)
      +                            result = self._execute(method, args, kwargs)
      +                            response.update(result)
      +
      +                except (TimeoutError, ConnectionError):
      +                    j.logger.debug(f"Client {address} closed the connection/or timeout", address)
      +                    parser.on_disconnect()
      +                    return
      +
      +                except Exception as exception:
      +                    j.logger.exception("internal error", exception=exception)
      +                    response["error"] = "internal server error"
      +                    response["error_type"] = GedisErrorTypes.INTERNAL_SERVER_ERROR.value
      +
      +                response["success"] = response["error"] is None
      +                encoder.encode(json.dumps(response, default=serialize))
      +
      +        except BrokenPipeError:
      +            pass
      +
      +

      Ancestors

      +
        +
      • Base
      • +
      • types.SimpleNamespace
      • +
      +

      Instance variables

      +
      +
      var actors
      +
      +

      Lists saved actors

      +

      Returns

      +

      list – List of saved actors

      +
      + +Expand source code + +
      @property
      +def actors(self):
      +    """Lists saved actors
      +
      +    Returns:
      +        list -- List of saved actors
      +    """
      +    return self._actors
      +
      +
      +
      var enable_system_actor
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var host
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var port
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var run_async
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def actor_add(self, actor_name: str, actor_path: str) +
      +
      +

      Adds an actor to the server

      +

      Arguments

      +

      actor_name {str} – Actor name +actor_path {str} – Actor absolute path

      +

      Raises

      +
      +
      j.exceptions.Value
      +
      raises if actor name is matched one of the reserved actor names
      +
      j.exceptions.Value
      +
      raises if actor name is not a valid identifier
      +
      +
      + +Expand source code + +
      def actor_add(self, actor_name: str, actor_path: str):
      +    """Adds an actor to the server
      +
      +    Arguments:
      +        actor_name {str} -- Actor name
      +        actor_path {str} -- Actor absolute path
      +
      +    Raises:
      +        j.exceptions.Value: raises if actor name is matched one of the reserved actor names
      +        j.exceptions.Value: raises if actor name is not a valid identifier
      +    """
      +    if actor_name in RESERVED_ACTOR_NAMES:
      +        raise j.exceptions.Value("Invalid actor name")
      +
      +    if not actor_name.isidentifier():
      +        raise j.exceptions.Value(f"Actor name should be a valid identifier")
      +
      +    self._actors[actor_name] = actor_path
      +
      +
      +
      +def actor_delete(self, actor_name: str) +
      +
      +

      Removes an actor from the server

      +

      Arguments

      +

      actor_name {str} – Actor name

      +
      + +Expand source code + +
      def actor_delete(self, actor_name: str):
      +    """Removes an actor from the server
      +
      +    Arguments:
      +        actor_name {str} -- Actor name
      +    """
      +    self._actors.pop(actor_name, None)
      +
      +
      +
      +def start(self) +
      +
      +

      Starts the server

      +
      + +Expand source code + +
      def start(self):
      +    """Starts the server
      +    """
      +    # register system actor if enabled
      +    if self.enable_system_actor:
      +        self._register_actor("system", self._system_actor)
      +
      +    self._core_actor.set_server(self)
      +    self._system_actor.set_server(self)
      +
      +    # register saved actors
      +    for actor_name, actor_path in self._actors.items():
      +        self._system_actor.register_actor(actor_name, actor_path)
      +
      +    # start the server
      +    self._server = StreamServer((self.host, self.port), self._on_connection, spawn=Pool())
      +    self._server.reuse_addr = True
      +    self._server.start()
      +
      +    j.logger.info(f"Gedis server is started at {self.host}:{self.port}...")
      +
      +
      +
      +def stop(self) +
      +
      +

      Stops the server

      +
      + +Expand source code + +
      def stop(self):
      +    """Stops the server
      +    """
      +    j.logger.info("Shutting down...")
      +    self._server.stop()
      +
      +
      +
      +

      Inherited members

      + +
      +
      +class RedisConnectionAdapter +(sock) +
      +
      +
      +
      + +Expand source code + +
      class RedisConnectionAdapter:
      +    def __init__(self, sock):
      +        self.socket = sock
      +        self._sock = sock
      +        self.socket_timeout = 600
      +        self.socket_connect_timeout = 600
      +        self.socket_keepalive = True
      +        self.retry_on_timeout = True
      +        self.socket_keepalive_options = {}
      +        self.encoder = Encoder("utf", "strict", False)
      +
      +
      +
      +class ResponseEncoder +(socket) +
      +
      +
      +
      + +Expand source code + +
      class ResponseEncoder:
      +    def __init__(self, socket):
      +        self.socket = socket
      +        self.buffer = BytesIO()
      +
      +    def encode(self, value):
      +        """Respond with data."""
      +        if value is None:
      +            self._write_buffer("$-1\r\n")
      +        elif isinstance(value, int):
      +            self._write_buffer(":{}\r\n".format(value))
      +        elif isinstance(value, bool):
      +            self._write_buffer(":{}\r\n".format(1 if value else 0))
      +        elif isinstance(value, str):
      +            if "\n" in value:
      +                self._bulk(value)
      +            else:
      +                self._write_buffer("+{}\r\n".format(value))
      +        elif isinstance(value, bytes):
      +            self._bulkbytes(value)
      +        elif isinstance(value, list):
      +            if value and value[0] == "*REDIS*":
      +                value = value[1:]
      +            self._array(value)
      +        elif hasattr(value, "__repr__"):
      +            self._bulk(value.__repr__())
      +        else:
      +            value = j.data.serializers.json.dumps(value, encoding="utf-8")
      +            self.encode(value)
      +
      +        self._send()
      +
      +    def status(self, msg="OK"):
      +        """Send a status."""
      +        self._write_buffer("+{}\r\n".format(msg))
      +        self._send()
      +
      +    def error(self, msg):
      +        """Send an error."""
      +        # print("###:%s" % msg)
      +        self._write_buffer("-ERR {}\r\n".format(msg))
      +        self._send()
      +
      +    def _bulk(self, value):
      +        """Send part of a multiline reply."""
      +        data = ["$", str(len(value)), "\r\n", value, "\r\n"]
      +        self._write_buffer("".join(data))
      +
      +    def _array(self, value):
      +        """send an array."""
      +        self._write_buffer("*{}\r\n".format(len(value)))
      +        for item in value:
      +            self.encode(item)
      +
      +    def _bulkbytes(self, value):
      +        data = [b"$", str(len(value)).encode(), b"\r\n", value, b"\r\n"]
      +        self._write_buffer(b"".join(data))
      +
      +    def _write_buffer(self, data):
      +        if isinstance(data, str):
      +            data = data.encode()
      +
      +        self.buffer.write(data)
      +
      +    def _send(self):
      +        self.socket.sendall(self.buffer.getvalue())
      +        self.buffer = BytesIO()  # seems faster then truncating
      +
      +

      Methods

      +
      +
      +def encode(self, value) +
      +
      +

      Respond with data.

      +
      + +Expand source code + +
      def encode(self, value):
      +    """Respond with data."""
      +    if value is None:
      +        self._write_buffer("$-1\r\n")
      +    elif isinstance(value, int):
      +        self._write_buffer(":{}\r\n".format(value))
      +    elif isinstance(value, bool):
      +        self._write_buffer(":{}\r\n".format(1 if value else 0))
      +    elif isinstance(value, str):
      +        if "\n" in value:
      +            self._bulk(value)
      +        else:
      +            self._write_buffer("+{}\r\n".format(value))
      +    elif isinstance(value, bytes):
      +        self._bulkbytes(value)
      +    elif isinstance(value, list):
      +        if value and value[0] == "*REDIS*":
      +            value = value[1:]
      +        self._array(value)
      +    elif hasattr(value, "__repr__"):
      +        self._bulk(value.__repr__())
      +    else:
      +        value = j.data.serializers.json.dumps(value, encoding="utf-8")
      +        self.encode(value)
      +
      +    self._send()
      +
      +
      +
      +def error(self, msg) +
      +
      +

      Send an error.

      +
      + +Expand source code + +
      def error(self, msg):
      +    """Send an error."""
      +    # print("###:%s" % msg)
      +    self._write_buffer("-ERR {}\r\n".format(msg))
      +    self._send()
      +
      +
      +
      +def status(self, msg='OK') +
      +
      +

      Send a status.

      +
      + +Expand source code + +
      def status(self, msg="OK"):
      +    """Send a status."""
      +    self._write_buffer("+{}\r\n".format(msg))
      +    self._send()
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis/systemactor.html b/docs/api/jumpscale/servers/gedis/systemactor.html new file mode 100644 index 000000000..ee02fb67f --- /dev/null +++ b/docs/api/jumpscale/servers/gedis/systemactor.html @@ -0,0 +1,392 @@ + + + + + + +jumpscale.servers.gedis.systemactor API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.servers.gedis.systemactor

      +
      +
      +
      + +Expand source code + +
      import os
      +import sys
      +import json
      +import inspect
      +from jumpscale.loader import j
      +from jumpscale.servers.gedis.baseactor import BaseActor, actor_method
      +
      +
      +class CoreActor(BaseActor):
      +    def __init__(self):
      +        super().__init__()
      +        self._server = None
      +        self.path = __file__
      +
      +    def set_server(self, server):
      +        self._server = server
      +
      +    @actor_method
      +    def list_actors(self) -> list:
      +        """List available actors
      +
      +        Returns:
      +            list -- list of available actors
      +        """
      +        return list(self._server._loaded_actors.keys())
      +
      +
      +class SystemActor(BaseActor):
      +    def __init__(self):
      +        super().__init__()
      +        self._server = None
      +        self.path = __file__
      +
      +    def set_server(self, server):
      +        self._server = server
      +
      +    @actor_method
      +    def register_actor(self, actor_name: str, actor_path: str, force_reload: bool = False) -> bool:
      +        """
      +        Register new actor
      +
      +        Args:
      +            actor_name (str): actor name within gedis server.
      +            actor_path (str): actor path on gedis server machine.
      +            force_reload (bool, optional): reload the module if set. Defaults to False.
      +
      +        Raises:
      +            j.exceptions.Validation: in case the actor is not valid
      +
      +        Returns:
      +            bool: True if registered
      +        """
      +        module = j.tools.codeloader.load_python_module(actor_path, force_reload=force_reload)
      +        actor = module.Actor()
      +        actor.path = actor_path
      +        result = actor.__validate_actor__()
      +
      +        if not result["valid"]:
      +            raise j.exceptions.Validation(
      +                "Actor {} is not valid, check the following errors {}".format(actor_name, result["errors"])
      +            )
      +
      +        self._server._register_actor(actor_name, actor)
      +        return True
      +
      +    @actor_method
      +    def unregister_actor(self, actor_name: str) -> bool:
      +        """Register actor
      +
      +        Arguments:
      +            actor_name {str} -- actor name
      +
      +        Returns:
      +            bool -- True if actors is unregistered
      +        """
      +        self._server._unregister_actor(actor_name)
      +        return True
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class CoreActor +
      +
      +
      +
      + +Expand source code + +
      class CoreActor(BaseActor):
      +    def __init__(self):
      +        super().__init__()
      +        self._server = None
      +        self.path = __file__
      +
      +    def set_server(self, server):
      +        self._server = server
      +
      +    @actor_method
      +    def list_actors(self) -> list:
      +        """List available actors
      +
      +        Returns:
      +            list -- list of available actors
      +        """
      +        return list(self._server._loaded_actors.keys())
      +
      +

      Ancestors

      + +

      Methods

      +
      +
      +def list_actors(self) ‑> list +
      +
      +

      List available actors

      +

      Returns

      +

      list – list of available actors

      +
      + +Expand source code + +
      @actor_method
      +def list_actors(self) -> list:
      +    """List available actors
      +
      +    Returns:
      +        list -- list of available actors
      +    """
      +    return list(self._server._loaded_actors.keys())
      +
      +
      +
      +def set_server(self, server) +
      +
      +
      +
      + +Expand source code + +
      def set_server(self, server):
      +    self._server = server
      +
      +
      +
      +
      +
      +class SystemActor +
      +
      +
      +
      + +Expand source code + +
      class SystemActor(BaseActor):
      +    def __init__(self):
      +        super().__init__()
      +        self._server = None
      +        self.path = __file__
      +
      +    def set_server(self, server):
      +        self._server = server
      +
      +    @actor_method
      +    def register_actor(self, actor_name: str, actor_path: str, force_reload: bool = False) -> bool:
      +        """
      +        Register new actor
      +
      +        Args:
      +            actor_name (str): actor name within gedis server.
      +            actor_path (str): actor path on gedis server machine.
      +            force_reload (bool, optional): reload the module if set. Defaults to False.
      +
      +        Raises:
      +            j.exceptions.Validation: in case the actor is not valid
      +
      +        Returns:
      +            bool: True if registered
      +        """
      +        module = j.tools.codeloader.load_python_module(actor_path, force_reload=force_reload)
      +        actor = module.Actor()
      +        actor.path = actor_path
      +        result = actor.__validate_actor__()
      +
      +        if not result["valid"]:
      +            raise j.exceptions.Validation(
      +                "Actor {} is not valid, check the following errors {}".format(actor_name, result["errors"])
      +            )
      +
      +        self._server._register_actor(actor_name, actor)
      +        return True
      +
      +    @actor_method
      +    def unregister_actor(self, actor_name: str) -> bool:
      +        """Register actor
      +
      +        Arguments:
      +            actor_name {str} -- actor name
      +
      +        Returns:
      +            bool -- True if actors is unregistered
      +        """
      +        self._server._unregister_actor(actor_name)
      +        return True
      +
      +

      Ancestors

      + +

      Methods

      +
      +
      +def register_actor(self, actor_name: str, actor_path: str, force_reload: bool = False) ‑> bool +
      +
      +

      Register new actor

      +

      Args

      +
      +
      actor_name : str
      +
      actor name within gedis server.
      +
      actor_path : str
      +
      actor path on gedis server machine.
      +
      force_reload : bool, optional
      +
      reload the module if set. Defaults to False.
      +
      +

      Raises

      +
      +
      j.exceptions.Validation
      +
      in case the actor is not valid
      +
      +

      Returns

      +
      +
      bool
      +
      True if registered
      +
      +
      + +Expand source code + +
      @actor_method
      +def register_actor(self, actor_name: str, actor_path: str, force_reload: bool = False) -> bool:
      +    """
      +    Register new actor
      +
      +    Args:
      +        actor_name (str): actor name within gedis server.
      +        actor_path (str): actor path on gedis server machine.
      +        force_reload (bool, optional): reload the module if set. Defaults to False.
      +
      +    Raises:
      +        j.exceptions.Validation: in case the actor is not valid
      +
      +    Returns:
      +        bool: True if registered
      +    """
      +    module = j.tools.codeloader.load_python_module(actor_path, force_reload=force_reload)
      +    actor = module.Actor()
      +    actor.path = actor_path
      +    result = actor.__validate_actor__()
      +
      +    if not result["valid"]:
      +        raise j.exceptions.Validation(
      +            "Actor {} is not valid, check the following errors {}".format(actor_name, result["errors"])
      +        )
      +
      +    self._server._register_actor(actor_name, actor)
      +    return True
      +
      +
      +
      +def set_server(self, server) +
      +
      +
      +
      + +Expand source code + +
      def set_server(self, server):
      +    self._server = server
      +
      +
      +
      +def unregister_actor(self, actor_name: str) ‑> bool +
      +
      +

      Register actor

      +

      Arguments

      +

      actor_name {str} – actor name

      +

      Returns

      +

      bool – True if actors is unregistered

      +
      + +Expand source code + +
      @actor_method
      +def unregister_actor(self, actor_name: str) -> bool:
      +    """Register actor
      +
      +    Arguments:
      +        actor_name {str} -- actor name
      +
      +    Returns:
      +        bool -- True if actors is unregistered
      +    """
      +    self._server._unregister_actor(actor_name)
      +    return True
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/gedis_http/index.html b/docs/api/jumpscale/servers/gedis_http/index.html new file mode 100644 index 000000000..1c6e254d1 --- /dev/null +++ b/docs/api/jumpscale/servers/gedis_http/index.html @@ -0,0 +1,507 @@ + + + + + + +jumpscale.servers.gedis_http API documentation + + + + + + + + + + + +
      +
      +
      +

      Module jumpscale.servers.gedis_http

      +
      +
      +
      + +Expand source code + +
      import json
      +from jumpscale.core.base import Base, fields
      +from jumpscale.loader import j
      +from gevent.pool import Pool
      +from bottle import Bottle, abort, request, response
      +from jumpscale.servers.gedis.server import GedisErrorTypes
      +from gevent.pywsgi import WSGIServer
      +from jumpscale.core.base import StoredFactory
      +
      +
      +class GedisHTTPServer(Base):
      +    host = fields.String(default="127.0.0.1")
      +    port = fields.Integer(default=8000)
      +    allow_cors = fields.Boolean(default=True)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._app = Bottle()
      +        self._client = None
      +        http_methods = ["GET", "POST"]
      +        if self.allow_cors:
      +            http_methods.extend(["OPTIONS", "PUT", "DELETE"])
      +        self._app.route("/<package>/<actor>/<method>", http_methods, self.enable_cors(self.handler, self.allow_cors))
      +
      +    @property
      +    def client(self):
      +        if self._client is None:
      +            self._client = j.clients.gedis.get(self.instance_name)
      +            self._client.disable_deserialization = True
      +        return self._client
      +
      +    def make_response(self, code, content):
      +        response.status = code
      +        response.content_type = "application/json"
      +        return json.dumps(content)
      +
      +    def enable_cors(self, fn, allow_cors=True):
      +        def _enable_cors(*args, **kwargs):
      +            # set CORS headers
      +            response.headers["Access-Control-Allow-Origin"] = "*"
      +            response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, OPTIONS, DELETE"
      +            response.headers[
      +                "Access-Control-Allow-Headers"
      +            ] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token"
      +
      +            if request.method != "OPTIONS":
      +                # actual request; reply with the actual response
      +                return fn(*args, **kwargs)
      +
      +        if allow_cors:
      +            return _enable_cors
      +        else:
      +            return fn
      +
      +    def handler(self, package, actor, method):
      +        actors = self.client.actors
      +
      +        actor = getattr(actors, f"{package}_{actor}", None)
      +        if not actor:
      +            return self.make_response(400, {"error": "actor not found"})
      +
      +        method = getattr(actor, method, None)
      +        if not method:
      +            return self.make_response(400, {"error": "method not found"})
      +
      +        kwargs = request.json or dict()
      +        response = method(**kwargs)
      +
      +        if not response.success:
      +            if response.error_type == GedisErrorTypes.NOT_FOUND:
      +                return self.make_response(404, {"error": response.error})
      +
      +            elif response.error_type == GedisErrorTypes.BAD_REQUEST:
      +                return self.make_response(400, {"error": response.error})
      +
      +            elif response.error_type == GedisErrorTypes.PERMISSION_ERROR:
      +                return self.make_response(403, {"error": response.error})
      +
      +            else:
      +                return self.make_response(500, {"error": response.error})
      +
      +        return self.make_response(200, response.result)
      +
      +    @property
      +    def gevent_server(self):
      +        return WSGIServer((self.host, self.port), self._app, spawn=Pool())
      +
      +
      +def export_module_as():
      +    return StoredFactory(GedisHTTPServer)
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def export_module_as() +
      +
      +
      +
      + +Expand source code + +
      def export_module_as():
      +    return StoredFactory(GedisHTTPServer)
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class GedisHTTPServer +(*args, **kwargs) +
      +
      +

      A simple attribute-based namespace.

      +

      SimpleNamespace(**kwargs)

      +

      base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

      +

      any instance can have an optional name and a parent.

      +
      class Person(Base):
      +    name = fields.String()
      +    age = fields.Float()
      +
      +p = Person(name="ahmed", age="19")
      +print(p.name, p.age)
      +
      +

      Args

      +
      +
      parent_ : Base, optional
      +
      parent instance. Defaults to None.
      +
      instance_name_ : str, optional
      +
      instance name. Defaults to None.
      +
      **values
      +
      any given field values to initiate the instance with
      +
      +
      + +Expand source code + +
      class GedisHTTPServer(Base):
      +    host = fields.String(default="127.0.0.1")
      +    port = fields.Integer(default=8000)
      +    allow_cors = fields.Boolean(default=True)
      +
      +    def __init__(self, *args, **kwargs):
      +        super().__init__(*args, **kwargs)
      +        self._app = Bottle()
      +        self._client = None
      +        http_methods = ["GET", "POST"]
      +        if self.allow_cors:
      +            http_methods.extend(["OPTIONS", "PUT", "DELETE"])
      +        self._app.route("/<package>/<actor>/<method>", http_methods, self.enable_cors(self.handler, self.allow_cors))
      +
      +    @property
      +    def client(self):
      +        if self._client is None:
      +            self._client = j.clients.gedis.get(self.instance_name)
      +            self._client.disable_deserialization = True
      +        return self._client
      +
      +    def make_response(self, code, content):
      +        response.status = code
      +        response.content_type = "application/json"
      +        return json.dumps(content)
      +
      +    def enable_cors(self, fn, allow_cors=True):
      +        def _enable_cors(*args, **kwargs):
      +            # set CORS headers
      +            response.headers["Access-Control-Allow-Origin"] = "*"
      +            response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, OPTIONS, DELETE"
      +            response.headers[
      +                "Access-Control-Allow-Headers"
      +            ] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token"
      +
      +            if request.method != "OPTIONS":
      +                # actual request; reply with the actual response
      +                return fn(*args, **kwargs)
      +
      +        if allow_cors:
      +            return _enable_cors
      +        else:
      +            return fn
      +
      +    def handler(self, package, actor, method):
      +        actors = self.client.actors
      +
      +        actor = getattr(actors, f"{package}_{actor}", None)
      +        if not actor:
      +            return self.make_response(400, {"error": "actor not found"})
      +
      +        method = getattr(actor, method, None)
      +        if not method:
      +            return self.make_response(400, {"error": "method not found"})
      +
      +        kwargs = request.json or dict()
      +        response = method(**kwargs)
      +
      +        if not response.success:
      +            if response.error_type == GedisErrorTypes.NOT_FOUND:
      +                return self.make_response(404, {"error": response.error})
      +
      +            elif response.error_type == GedisErrorTypes.BAD_REQUEST:
      +                return self.make_response(400, {"error": response.error})
      +
      +            elif response.error_type == GedisErrorTypes.PERMISSION_ERROR:
      +                return self.make_response(403, {"error": response.error})
      +
      +            else:
      +                return self.make_response(500, {"error": response.error})
      +
      +        return self.make_response(200, response.result)
      +
      +    @property
      +    def gevent_server(self):
      +        return WSGIServer((self.host, self.port), self._app, spawn=Pool())
      +
      +

      Ancestors

      +
        +
      • Base
      • +
      • types.SimpleNamespace
      • +
      +

      Instance variables

      +
      +
      var allow_cors
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var client
      +
      +
      +
      + +Expand source code + +
      @property
      +def client(self):
      +    if self._client is None:
      +        self._client = j.clients.gedis.get(self.instance_name)
      +        self._client.disable_deserialization = True
      +    return self._client
      +
      +
      +
      var gevent_server
      +
      +
      +
      + +Expand source code + +
      @property
      +def gevent_server(self):
      +    return WSGIServer((self.host, self.port), self._app, spawn=Pool())
      +
      +
      +
      var host
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      var port
      +
      +

      getter method this property

      +

      will call _get_value, which would if the value is already defined +and will get the default value if not

      +

      Returns

      +
      +
      any
      +
      the field value
      +
      +
      + +Expand source code + +
      def getter(self):
      +    """
      +    getter method this property
      +
      +    will call `_get_value`, which would if the value is already defined
      +    and will get the default value if not
      +
      +    Returns:
      +        any: the field value
      +    """
      +    return self._get_value(name, field)
      +
      +
      +
      +

      Methods

      +
      +
      +def enable_cors(self, fn, allow_cors=True) +
      +
      +
      +
      + +Expand source code + +
      def enable_cors(self, fn, allow_cors=True):
      +    def _enable_cors(*args, **kwargs):
      +        # set CORS headers
      +        response.headers["Access-Control-Allow-Origin"] = "*"
      +        response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, OPTIONS, DELETE"
      +        response.headers[
      +            "Access-Control-Allow-Headers"
      +        ] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token"
      +
      +        if request.method != "OPTIONS":
      +            # actual request; reply with the actual response
      +            return fn(*args, **kwargs)
      +
      +    if allow_cors:
      +        return _enable_cors
      +    else:
      +        return fn
      +
      +
      +
      +def handler(self, package, actor, method) +
      +
      +
      +
      + +Expand source code + +
      def handler(self, package, actor, method):
      +    actors = self.client.actors
      +
      +    actor = getattr(actors, f"{package}_{actor}", None)
      +    if not actor:
      +        return self.make_response(400, {"error": "actor not found"})
      +
      +    method = getattr(actor, method, None)
      +    if not method:
      +        return self.make_response(400, {"error": "method not found"})
      +
      +    kwargs = request.json or dict()
      +    response = method(**kwargs)
      +
      +    if not response.success:
      +        if response.error_type == GedisErrorTypes.NOT_FOUND:
      +            return self.make_response(404, {"error": response.error})
      +
      +        elif response.error_type == GedisErrorTypes.BAD_REQUEST:
      +            return self.make_response(400, {"error": response.error})
      +
      +        elif response.error_type == GedisErrorTypes.PERMISSION_ERROR:
      +            return self.make_response(403, {"error": response.error})
      +
      +        else:
      +            return self.make_response(500, {"error": response.error})
      +
      +    return self.make_response(200, response.result)
      +
      +
      +
      +def make_response(self, code, content) +
      +
      +
      +
      + +Expand source code + +
      def make_response(self, code, content):
      +    response.status = code
      +    response.content_type = "application/json"
      +    return json.dumps(content)
      +
      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/index.html b/docs/api/jumpscale/servers/index.html index 714d4ee17..e0374dbe4 100644 --- a/docs/api/jumpscale/servers/index.html +++ b/docs/api/jumpscale/servers/index.html @@ -26,6 +26,18 @@

      Namespace jumpscale.servers

      Sub-modules

      +
      jumpscale.servers.appserver
      +
      +
      +
      +
      jumpscale.servers.gedis
      +
      +

      Gedis server package provides all code needed to have an RPC server using redis protocol for messaging …

      +
      +
      jumpscale.servers.gedis_http
      +
      +
      +
      jumpscale.servers.openresty
      @@ -34,6 +46,10 @@

      Sub-modules

      +
      jumpscale.servers.threebot
      +
      +
      +
      @@ -56,8 +72,12 @@

      Index

    • Sub-modules

    @@ -67,4 +87,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/openresty/index.html b/docs/api/jumpscale/servers/openresty/index.html index b58315462..3b73ce892 100644 --- a/docs/api/jumpscale/servers/openresty/index.html +++ b/docs/api/jumpscale/servers/openresty/index.html @@ -106,4 +106,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/openresty/location.html b/docs/api/jumpscale/servers/openresty/location.html index 723dfd515..c964c05bb 100644 --- a/docs/api/jumpscale/servers/openresty/location.html +++ b/docs/api/jumpscale/servers/openresty/location.html @@ -688,4 +688,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/openresty/server.html b/docs/api/jumpscale/servers/openresty/server.html index ef967bad8..1b54e2fdb 100644 --- a/docs/api/jumpscale/servers/openresty/server.html +++ b/docs/api/jumpscale/servers/openresty/server.html @@ -1035,4 +1035,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/openresty/utils.html b/docs/api/jumpscale/servers/openresty/utils.html index b7f297d24..7a0d50656 100644 --- a/docs/api/jumpscale/servers/openresty/utils.html +++ b/docs/api/jumpscale/servers/openresty/utils.html @@ -85,4 +85,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/rack/index.html b/docs/api/jumpscale/servers/rack/index.html index 580003f9f..532344c22 100644 --- a/docs/api/jumpscale/servers/rack/index.html +++ b/docs/api/jumpscale/servers/rack/index.html @@ -92,4 +92,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/rack/rack.html b/docs/api/jumpscale/servers/rack/rack.html index 76d2fed4a..0e3caeb50 100644 --- a/docs/api/jumpscale/servers/rack/rack.html +++ b/docs/api/jumpscale/servers/rack/rack.html @@ -412,4 +412,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/threebot/index.html b/docs/api/jumpscale/servers/threebot/index.html new file mode 100644 index 000000000..d4c7eada4 --- /dev/null +++ b/docs/api/jumpscale/servers/threebot/index.html @@ -0,0 +1,288 @@ + + + + + + +jumpscale.servers.threebot API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.servers.threebot

    +
    +
    +
    + +Expand source code + +
    from jumpscale.core.base import StoredFactory
    +from jumpscale.sals.nginx.nginx import PORTS
    +from jumpscale.entry_points.threebot import create_wallets_if_not_exists
    +
    +
    +class ThreebotServerFactory(StoredFactory):
    +    default = None
    +
    +    def new(self, name, *args, **kwargs):
    +        if self.default:
    +            return self.default
    +        self.default = super().new("default", *args, **kwargs)
    +        return self.default
    +
    +    def get(self, name=None, *args, **kwargs):
    +        """get ThreebotServer default instance
    +
    +        Args:
    +            name: ignored but here to be same signature as parent
    +
    +        Returns:
    +            ThreebotServer
    +        """
    +        return super().get("default", *args, **kwargs)
    +
    +    def start_default(self, wait=False, local=False, domain=None, email=None, cert=True):
    +        PORTS.init_default_ports(local)
    +        server = self.get("default")
    +        if not server.domain:
    +            server.domain = domain
    +            server.email = email
    +        server.save()
    +        create_wallets_if_not_exists()
    +        server.start(wait=wait, cert=cert)
    +
    +
    +def export_module_as():
    +    from .threebot import ThreebotServer
    +
    +    return ThreebotServerFactory(ThreebotServer)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.servers.threebot.threebot
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from .threebot import ThreebotServer
    +
    +    return ThreebotServerFactory(ThreebotServer)
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class ThreebotServerFactory +(type_, name_=None, parent_instance_=None, parent_factory_=None) +
    +
    +

    Stored factories are a custom type of Factory, which uses current configured store backend +to store all instance configurations.

    +

    get a new stored factory given the type to create and store instances for.

    +

    Any factory can have a name, parent Base instance and a parent factory.

    +

    Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

    +

    Args

    +
    +
    type_ : Base
    +
    Base class type
    +
    name_ : str, optional
    +
    factory name. Defaults to None.
    +
    parent_instance_ : Base, optional
    +
    a parent Base instance. Defaults to None.
    +
    parent_factory_ : Factory, optional
    +
    a parent Factory. Defaults to None.
    +
    +
    + +Expand source code + +
    class ThreebotServerFactory(StoredFactory):
    +    default = None
    +
    +    def new(self, name, *args, **kwargs):
    +        if self.default:
    +            return self.default
    +        self.default = super().new("default", *args, **kwargs)
    +        return self.default
    +
    +    def get(self, name=None, *args, **kwargs):
    +        """get ThreebotServer default instance
    +
    +        Args:
    +            name: ignored but here to be same signature as parent
    +
    +        Returns:
    +            ThreebotServer
    +        """
    +        return super().get("default", *args, **kwargs)
    +
    +    def start_default(self, wait=False, local=False, domain=None, email=None, cert=True):
    +        PORTS.init_default_ports(local)
    +        server = self.get("default")
    +        if not server.domain:
    +            server.domain = domain
    +            server.email = email
    +        server.save()
    +        create_wallets_if_not_exists()
    +        server.start(wait=wait, cert=cert)
    +
    +

    Ancestors

    + +

    Subclasses

    +
      +
    • jumpscale.core.base.factory.ThreebotServerFactory
    • +
    +

    Class variables

    +
    +
    var default
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def get(self, name=None, *args, **kwargs) +
    +
    +

    get ThreebotServer default instance

    +

    Args

    +
    +
    name
    +
    ignored but here to be same signature as parent
    +
    +

    Returns

    +

    ThreebotServer

    +
    + +Expand source code + +
    def get(self, name=None, *args, **kwargs):
    +    """get ThreebotServer default instance
    +
    +    Args:
    +        name: ignored but here to be same signature as parent
    +
    +    Returns:
    +        ThreebotServer
    +    """
    +    return super().get("default", *args, **kwargs)
    +
    +
    +
    +def start_default(self, wait=False, local=False, domain=None, email=None, cert=True) +
    +
    +
    +
    + +Expand source code + +
    def start_default(self, wait=False, local=False, domain=None, email=None, cert=True):
    +    PORTS.init_default_ports(local)
    +    server = self.get("default")
    +    if not server.domain:
    +        server.domain = domain
    +        server.email = email
    +    server.save()
    +    create_wallets_if_not_exists()
    +    server.start(wait=wait, cert=cert)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/servers/threebot/threebot.html b/docs/api/jumpscale/servers/threebot/threebot.html new file mode 100644 index 000000000..95b5df9b9 --- /dev/null +++ b/docs/api/jumpscale/servers/threebot/threebot.html @@ -0,0 +1,3483 @@ + + + + + + +jumpscale.servers.threebot.threebot API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.servers.threebot.threebot

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +
    +import imp
    +import os
    +import sys
    +import toml
    +import shutil
    +import gevent
    +import signal
    +from urllib.parse import urlparse
    +from gevent.pywsgi import WSGIServer
    +from jumpscale.core.base import Base, fields
    +from jumpscale import packages as pkgnamespace
    +from jumpscale.sals.nginx.nginx import LocationType, PORTS
    +from jumpscale.sals.nginx.nginx import LocationType, PORTS, AcmeServer
    +from jumpscale.servers.appserver import StripPathMiddleware, apply_main_middlewares
    +
    +
    +GEDIS = "gedis"
    +GEDIS_HTTP = "gedis_http"
    +GEDIS_HTTP_HOST = "127.0.0.1"
    +GEDIS_HTTP_PORT = 8000
    +SERVICE_MANAGER = "service_manager"
    +CHATFLOW_SERVER_HOST = "127.0.0.1"
    +CHATFLOW_SERVER_PORT = 31000
    +DEFAULT_PACKAGES = {
    +    "auth": {"path": os.path.dirname(j.packages.auth.__file__), "giturl": ""},
    +    "chatflows": {"path": os.path.dirname(j.packages.chatflows.__file__), "giturl": ""},
    +    "admin": {"path": os.path.dirname(j.packages.admin.__file__), "giturl": ""},
    +    "weblibs": {"path": os.path.dirname(j.packages.weblibs.__file__), "giturl": ""},
    +    "backup": {"path": os.path.dirname(j.packages.backup.__file__), "giturl": ""},
    +}
    +DOWNLOADED_PACKAGES_PATH = j.sals.fs.join_paths(j.core.dirs.VARDIR, "downloaded_packages")
    +
    +
    +class NginxPackageConfig:
    +    def __init__(self, package):
    +        self.package = package
    +        self.nginx = j.sals.nginx.get("main")
    +
    +    @property
    +    def default_config(self):
    +        default_server = {
    +            "name": "default",
    +            "ports": self.package.config.get("ports"),
    +            "locations": self.package.config.get("locations", []),
    +            "domain": self.package.default_domain,
    +            "letsencryptemail": self.package.default_email,
    +            "acme_server_type": self.package.default_acme_server_type,
    +            "acme_server_url": self.package.default_acme_server_url,
    +        }
    +
    +        is_auth = self.package.config.get("is_auth", True)
    +        is_admin = self.package.config.get("is_admin", True)
    +        is_package_authorized = self.package.config.get("is_package_authorized", False)
    +
    +        for static_dir in self.package.static_dirs:
    +            path_url = j.data.text.removeprefix(static_dir.get("path_url"), "/")
    +            default_server["locations"].append(
    +                {
    +                    "is_auth": static_dir.get("is_auth", is_auth),
    +                    "is_admin": static_dir.get("is_admin", is_admin),
    +                    "is_package_authorized": static_dir.get("is_package_authorized", is_package_authorized),
    +                    "type": "static",
    +                    "name": static_dir.get("name"),
    +                    "spa": static_dir.get("spa"),
    +                    "index": static_dir.get("index"),
    +                    "path_url": j.sals.fs.join_paths(self.package.base_url, path_url),
    +                    "path_location": self.package.resolve_staticdir_location(static_dir),
    +                    "force_https": self.package.config.get("force_https", True),
    +                }
    +            )
    +
    +        for bottle_server in self.package.bottle_servers:
    +            path_url = j.data.text.removeprefix(bottle_server.get("path_url"), "/")
    +            if hasattr(bottle_server, "standalone") and bottle_server.standalone:
    +                default_server["locations"].append(
    +                    {
    +                        "is_auth": bottle_server.get("is_auth", is_auth),
    +                        "is_admin": bottle_server.get("is_admin", is_admin),
    +                        "is_package_authorized": bottle_server.get("is_package_authorized", is_package_authorized),
    +                        "type": "proxy",
    +                        "name": bottle_server.get("name"),
    +                        "host": bottle_server.get("host"),
    +                        "port": bottle_server.get("port"),
    +                        "path_url": j.sals.fs.join_paths(self.package.base_url,),
    +                        "path_dest": bottle_server.get("path_dest"),
    +                        "websocket": bottle_server.get("websocket"),
    +                        "force_https": self.package.config.get("force_https", True),
    +                    }
    +                )
    +            else:
    +                path_url = j.data.text.removeprefix(bottle_server.get("path_url"), "/")
    +                default_server["locations"].append(
    +                    {
    +                        "is_auth": bottle_server.get("is_auth", is_auth),
    +                        "is_admin": bottle_server.get("is_admin", is_admin),
    +                        "is_package_authorized": bottle_server.get("is_package_authorized", is_package_authorized),
    +                        "type": "proxy",
    +                        "name": bottle_server.get("name"),
    +                        "host": "0.0.0.0",
    +                        "port": 31000,
    +                        "path_url": j.sals.fs.join_paths(self.package.base_url, path_url),
    +                        "path_dest": f"/{self.package.name}{bottle_server.get('path_dest')}",
    +                        "websocket": bottle_server.get("websocket"),
    +                        "force_https": self.package.config.get("force_https", True),
    +                    }
    +                )
    +
    +        if self.package.actors_dir:
    +            default_server["locations"].append(
    +                {
    +                    "is_auth": is_auth,
    +                    "is_admin": is_admin,
    +                    "is_package_authorized": is_package_authorized,
    +                    "type": "proxy",
    +                    "name": "actors",
    +                    "host": GEDIS_HTTP_HOST,
    +                    "port": GEDIS_HTTP_PORT,
    +                    "path_url": j.sals.fs.join_paths(self.package.base_url, "actors"),
    +                    "path_dest": self.package.base_url,
    +                    "force_https": self.package.config.get("force_https", True),
    +                }
    +            )
    +
    +        if self.package.chats_dir:
    +            default_server["locations"].append(
    +                {
    +                    "is_auth": is_auth,
    +                    "is_admin": is_admin,
    +                    "is_package_authorized": is_package_authorized,
    +                    "type": "proxy",
    +                    "name": "chats",
    +                    "host": CHATFLOW_SERVER_HOST,
    +                    "port": CHATFLOW_SERVER_PORT,
    +                    "path_url": j.sals.fs.join_paths(self.package.base_url, "chats"),
    +                    "path_dest": f"/chatflows{self.package.base_url}/chats",  # TODO: temperoary fix for auth package
    +                    "force_https": self.package.config.get("force_https", True),
    +                }
    +            )
    +
    +        return [default_server]
    +
    +    def apply(self, write_config=True):
    +        default_ports = [PORTS.HTTP, PORTS.HTTPS]
    +        servers = self.default_config + self.package.config.get("servers", [])
    +        for server in servers:
    +            ports = server.get("ports", default_ports) or default_ports
    +            for port in ports:
    +                server_name = server.get("name")
    +                if server_name != "default":
    +                    server_name = f"{self.package.name}_{server_name}"
    +
    +                website = self.nginx.get_website(server_name, port=port)
    +                website.ssl = server.get("ssl", port == PORTS.HTTPS)
    +                website.includes = server.get("includes", [])
    +                website.domain = server.get("domain", self.default_config[0].get("domain"))
    +                website.letsencryptemail = server.get(
    +                    "letsencryptemail", self.default_config[0].get("letsencryptemail")
    +                )
    +                website.acme_server_type = server.get(
    +                    "acme_server_type", self.default_config[0].get("acme_server_type")
    +                )
    +                website.acme_server_url = server.get("acme_server_url", self.default_config[0].get("acme_server_url"))
    +                if server.get("key_path"):
    +                    website.key_path = server["key_path"]
    +                if server.get("cert_path"):
    +                    website.cert_path = server["cert_path"]
    +                if server.get("fullchain_path"):
    +                    website.fullchain_path = server["fullchain_path"]
    +
    +                for location in server.get("locations", []):
    +                    loc = None
    +
    +                    location_name = location.get("name")
    +                    location_name = f"{self.package.name}_{location_name}"
    +                    location_type = location.get("type", "static")
    +
    +                    if location_type == "static":
    +                        loc = website.get_static_location(location_name)
    +                        loc.spa = location.get("spa", False)
    +                        loc.index = location.get("index")
    +                        loc.path_location = location.get("path_location")
    +
    +                    elif location_type == "proxy":
    +                        loc = website.get_proxy_location(location_name)
    +                        loc.scheme = location.get("scheme", "http")
    +                        loc.host = location.get("host")
    +                        loc.port = location.get("port", PORTS.HTTP)
    +                        loc.path_dest = location.get("path_dest", "")
    +                        loc.websocket = location.get("websocket", False)
    +                        loc.proxy_buffering = location.get("proxy_buffering", "")
    +                        loc.proxy_buffers = location.get("proxy_buffers")
    +                        loc.proxy_buffer_size = location.get("proxy_buffer_size")
    +
    +                    elif location_type == "custom":
    +                        loc = website.get_custom_location(location_name)
    +                        loc.custom_config = location.get("custom_config")
    +
    +                    if loc:
    +                        loc.location_type = location_type
    +                        path_url = location.get("path_url", "/")
    +                        if loc.location_type == LocationType.PROXY:
    +                            # proxy location needs / (as we append slash to the backend server too)
    +                            # and nginx always redirects to the same location with slash
    +                            # this way, requests will go to backend servers without double slashes...etc
    +                            if not path_url.endswith("/"):
    +                                path_url += "/"
    +                        else:
    +                            # for other locations, slash is not required
    +                            if path_url != "/":
    +                                path_url = path_url.rstrip("/")
    +
    +                        loc.path_url = path_url
    +                        loc.force_https = location.get("force_https")
    +                        loc.is_auth = location.get("is_auth", False)
    +                        loc.is_admin = location.get("is_admin", False)
    +                        loc.is_package_authorized = location.get("is_package_authorized", False)
    +                        loc.package_name = self.package.name
    +                if write_config:
    +                    website.configure(generate_certificates=self.nginx.cert)
    +
    +
    +class Package:
    +    def __init__(
    +        self,
    +        path,
    +        default_domain,
    +        default_email,
    +        giturl="",
    +        kwargs=None,
    +        admins=None,
    +        default_acme_server_type=AcmeServer.LETSENCRYPT,
    +        default_acme_server_url=None,
    +    ):
    +        self.path = path
    +        self.giturl = giturl
    +        self._config = None
    +        self.name = j.sals.fs.basename(path.rstrip("/"))
    +        self.nginx_config = NginxPackageConfig(self)
    +        self._module = None
    +        self.default_domain = default_domain
    +        self.default_email = default_email
    +        self.default_acme_server_type = default_acme_server_type
    +        self.default_acme_server_url = default_acme_server_url
    +        self.kwargs = kwargs or {}
    +        self.admins = admins or []
    +
    +    def _load_files(self, dir_path):
    +        for file_path in j.sals.fs.walk_files(dir_path, recursive=False):
    +            file_name = j.sals.fs.basename(file_path)
    +            if file_name.endswith(".py"):
    +                name = f"{self.name}_{file_name[:-3]}"
    +                yield dict(name=name, path=file_path)
    +
    +    def load_config(self):
    +        return toml.load(self.package_config_path)
    +
    +    @property
    +    def package_config_path(self):
    +        return j.sals.fs.join_paths(self.path, "package.toml")
    +
    +    @property
    +    def package_module_path(self):
    +        return j.sals.fs.join_paths(self.path, "package.py")
    +
    +    @property
    +    def module(self):
    +        if self._module is None:
    +            package_file_path = j.sals.fs.join_paths(self.path, "package.py")
    +            if j.sals.fs.exists(package_file_path):
    +                module = imp.load_source(self.name, package_file_path)
    +                if not hasattr(module, self.name):
    +                    raise j.exceptions.Halt(f"missing class ({self.name}) in the package file")
    +
    +                self._module = getattr(module, self.name)()
    +        return self._module
    +
    +    @property
    +    def base_url(self):
    +        return j.sals.fs.join_paths("/", self.name)
    +
    +    @property
    +    def config(self):
    +        if not self._config:
    +            self._config = self.load_config()
    +        return self._config
    +
    +    @property
    +    def ui_name(self):
    +        return self.config.get("ui_name", self.name)
    +
    +    @property
    +    def actors_dir(self):
    +        actors_dir = j.sals.fs.join_paths(self.path, self.config.get("actors_dir", "actors"))
    +        if j.sals.fs.exists(actors_dir):
    +            return actors_dir
    +
    +    @property
    +    def chats_dir(self):
    +        chats_dir = j.sals.fs.join_paths(self.path, self.config.get("chats_dir", "chats"))
    +        if j.sals.fs.exists(chats_dir):
    +            return chats_dir
    +
    +    @property
    +    def services_dir(self):
    +        services_dir = j.sals.fs.join_paths(self.path, self.config.get("services_dir", "services"))
    +        if j.sals.fs.exists(services_dir):
    +            return services_dir
    +
    +    @property
    +    def static_dirs(self):
    +        return self.config.get("static_dirs", [])
    +
    +    @property
    +    def bottle_servers(self):
    +        return self.config.get("bottle_servers", [])
    +
    +    @property
    +    def actors(self):
    +        return self._load_files(self.actors_dir)
    +
    +    @property
    +    def services(self):
    +        return self._load_files(self.services_dir)
    +
    +    def resolve_staticdir_location(self, static_dir):
    +        """Resolves path for static location in case we need it
    +        absoulute or not
    +
    +        static_dir.absolute_path true it will return the path directly
    +        if false will be relative to the path
    +
    +        Args:
    +            static_dir (str): package.toml static dirs category
    +
    +        Returns:
    +            str: package path
    +        """
    +        path_location = static_dir.get("path_location")
    +        absolute_path = static_dir.get("absolute_path", False)
    +        if absolute_path:
    +            return j.sals.fs.expanduser(path_location)
    +        return j.sals.fs.expanduser(j.sals.fs.join_paths(self.path, path_location))
    +
    +    def get_bottle_server(self, file_path, host, port):
    +        module = imp.load_source(file_path[:-3], file_path)
    +        return WSGIServer((host, port), StripPathMiddleware(module.app))
    +
    +    def get_package_bottle_app(self, file_path):
    +        module = imp.load_source(file_path[:-3], file_path)
    +        return module.app
    +
    +    def preinstall(self):
    +        if self.module and hasattr(self.module, "preinstall"):
    +            self.module.preinstall()
    +
    +    def install(self, **kwargs):
    +        if self.module and hasattr(self.module, "install"):
    +            self.module.install(**kwargs)
    +
    +    def uninstall(self):
    +        if self.module and hasattr(self.module, "uninstall"):
    +            self.module.uninstall()
    +
    +    def start(self):
    +        if self.module and hasattr(self.module, "start"):
    +            self.module.start(**self.kwargs)
    +
    +    def stop(self):
    +        if self.module and hasattr(self.module, "stop"):
    +            self.module.stop()
    +
    +    def restart(self):
    +        if self.module:
    +            self.module.stop()
    +            self.module.start()
    +
    +    def exists(self):
    +        return j.sals.fs.exists(self.package_config_path)
    +
    +    def is_valid(self):
    +        # more constraints, but for now let's say it's not ok if the main files don't exist
    +        return self.exists() and not self.is_excluded()
    +
    +    def is_excluded(self):
    +        return self.config.get("excluded", False) == True
    +
    +
    +class PackageManager(Base):
    +    packages = fields.Typed(dict, default=DEFAULT_PACKAGES.copy())
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._threebot = None
    +
    +    @property
    +    def threebot(self):
    +        if self._threebot is None:
    +            self._threebot = j.servers.threebot.get()
    +        return self._threebot
    +
    +    def get(self, package_name):
    +        if package_name in self.packages:
    +            package_path = self.packages[package_name]["path"]
    +            package_giturl = self.packages[package_name]["giturl"]
    +            package_kwargs = self.packages[package_name].get("kwargs", {})
    +            package_admins = self.packages[package_name].get("admins", [])
    +
    +            return Package(
    +                path=package_path,
    +                default_domain=self.threebot.domain,
    +                default_email=self.threebot.email,
    +                default_acme_server_type=self.threebot.acme_server_type,
    +                default_acme_server_url=self.threebot.acme_server_url,
    +                giturl=package_giturl,
    +                kwargs=package_kwargs,
    +                admins=package_admins,
    +            )
    +
    +    def get_packages(self):
    +        all_packages = []
    +
    +        # Add installed packages including outer packages
    +        for pkg in self.packages:
    +            package = self.get(pkg)
    +
    +            if package and package.is_valid():
    +                if j.sals.fs.exists(package.path):
    +                    chatflows = True if package.chats_dir else False
    +                    all_packages.append(
    +                        {
    +                            "name": pkg,
    +                            "path": package.path,
    +                            "giturl": package.giturl,
    +                            "system_package": pkg in DEFAULT_PACKAGES.keys(),
    +                            "installed": True,
    +                            "frontend": package.config.get("frontend", False),
    +                            "chatflows": chatflows,
    +                            "ui_name": package.ui_name,
    +                        }
    +                    )
    +                else:
    +                    j.logger.error(f"path {package.path} for {pkg} doesn't exist anymore")
    +            else:
    +                j.logger.error("pkg {pkg} is in self.packages but it's None")
    +
    +        # Add uninstalled sdk packages under j.packages
    +        for path in set(pkgnamespace.__path__):
    +            for pkg in os.listdir(path):
    +                pkg_path = j.sals.fs.join_paths(path, pkg)
    +                pkgtoml_path = j.sals.fs.join_paths(pkg_path, "package.toml")
    +                ui_name = pkg
    +                excluded = False
    +                with open(pkgtoml_path) as f:
    +                    conf = j.data.serializers.toml.loads(f.read())
    +                    ui_name = conf.get("ui_name", pkg)
    +                    excluded = conf.get("excluded", False)
    +                if pkg not in self.packages and j.sals.fs.exists(pkgtoml_path) and not excluded:
    +                    all_packages.append(
    +                        {
    +                            "name": pkg,
    +                            "path": j.sals.fs.dirname(getattr(j.packages, pkg).__file__),
    +                            "giturl": "",
    +                            "system_package": pkg in DEFAULT_PACKAGES.keys(),
    +                            "installed": False,
    +                            "ui_name": ui_name,
    +                        }
    +                    )
    +
    +        return all_packages
    +
    +    def list_all(self):
    +        return list(self.packages.keys())
    +
    +    def add(self, path: str = None, giturl: str = None, **kwargs):
    +        # first check if public repo
    +        # TODO: Check if package already exists
    +        if not any([path, giturl]) or all([path, giturl]):
    +            raise j.exceptions.Value("either path or giturl is required")
    +        pkg_name = ""
    +        if giturl:
    +            url = urlparse(giturl)
    +            url_parts = url.path.lstrip("/").split("/")
    +            if len(url_parts) == 2:
    +                pkg_name = url_parts[1].strip("/")
    +                j.logger.debug(
    +                    f"user didn't pass a URL containing branch {giturl}, try to guess (master, main, development) in order"
    +                )
    +                if j.tools.http.get(f"{giturl}/tree/master").status_code == 200:
    +                    url_parts.extend(["tree", "master"])
    +                elif j.tools.http.get(f"{giturl}/tree/main").status_code == 200:
    +                    url_parts.extend(["tree", "main"])
    +                elif j.tools.http.get(f"{giturl}/tree/development").status_code == 200:
    +                    url_parts.extend(["tree", "development"])
    +                else:
    +                    raise j.exceptions.Value(f"couldn't guess the branch for {giturl}")
    +            else:
    +                pkg_name = url_parts[-1].strip("/")
    +
    +            if len(url_parts) < 4:
    +                raise j.exceptions.Value(f"invalid git URL {giturl}")
    +
    +            org, repo, _, branch = url_parts[:4]
    +            repo_dir = f"{org}_{repo}_{pkg_name}_{branch}"
    +            repo_path = j.sals.fs.join_paths(DOWNLOADED_PACKAGES_PATH, repo_dir)
    +            repo_url = f"{url.scheme}://{url.hostname}/{org}/{repo}"
    +
    +            # delete repo dir if exists
    +            j.sals.fs.rmtree(repo_path)
    +
    +            j.tools.git.clone_repo(url=repo_url, dest=repo_path, branch_or_tag=branch)
    +            toml_paths = list(
    +                j.sals.fs.walk(repo_path, "*", filter_fun=lambda x: str(x).endswith(f"{pkg_name}/package.toml"))
    +            )
    +            if not toml_paths:
    +                raise j.exceptions.Value(f"couldn't find {pkg_name}/package.toml in {repo_path}")
    +            path_for_package_toml = toml_paths[0]
    +            package_path = j.sals.fs.parent(path_for_package_toml)
    +            path = package_path
    +
    +        admins = kwargs.pop("admins", [])
    +
    +        package = Package(
    +            path=path,
    +            default_domain=self.threebot.domain,
    +            default_email=self.threebot.email,
    +            giturl=giturl,
    +            kwargs=kwargs,
    +            admins=admins,
    +        )
    +
    +        # TODO: adding under the same name if same path and same giturl should be fine, no?
    +        # if package.name in self.packages:
    +        #     raise j.exceptions.Value(f"Package with name {package.name} already exists")
    +
    +        # execute package install method
    +        package.install(**kwargs)
    +
    +        # install package if threebot is started
    +        if self.threebot.started:
    +            self.install(package)
    +            self.threebot.nginx.reload()
    +        self.packages[package.name] = {
    +            "name": package.name,
    +            "path": package.path,
    +            "giturl": package.giturl,
    +            "kwargs": package.kwargs,
    +            "admins": package.admins,
    +            "ui_name": package.ui_name,
    +        }
    +
    +        self.save()
    +
    +        # Return updated package info
    +        return {package.name: self.packages[package.name]}
    +
    +    def delete(self, package_name):
    +        if package_name in DEFAULT_PACKAGES:
    +            raise j.exceptions.Value("cannot delete default packages")
    +        package = self.get(package_name)
    +        if not package:
    +            raise j.exceptions.NotFound(f"{package_name} package not found")
    +
    +        # remove bottle servers
    +        rack_servers = list(self.threebot.rack._servers)
    +        for bottle_server in rack_servers:
    +            if bottle_server.startswith(f"{package_name}_"):
    +                self.threebot.rack.remove(bottle_server)
    +
    +        # stop background services
    +        if package.services_dir:
    +            for service in package.services:
    +                self.threebot.services.stop_service(service["name"])
    +
    +        if self.threebot.started:
    +            # unregister gedis actors
    +            gedis_actors = list(self.threebot.gedis._loaded_actors.keys())
    +            for actor in gedis_actors:
    +                if actor.startswith(f"{package_name}_"):
    +                    self.threebot.gedis._system_actor.unregister_actor(actor)
    +
    +            # unload chats
    +            try:
    +                if package.chats_dir:
    +                    self.threebot.chatbot.unload(package.chats_dir)
    +            except Exception as e:
    +                j.logger.warning(f"Couldn't unload the chats of package {package_name}, this is the exception {str(e)}")
    +
    +            # reload nginx
    +            self.threebot.nginx.reload()
    +
    +        # execute package uninstall method
    +        package.uninstall()
    +
    +        self.packages.pop(package_name)
    +        self.save()
    +
    +    def install(self, package):
    +        """install and apply package configrations
    +
    +        Args:
    +            package ([package object]): get package object using [self.get(package_name)]
    +
    +        Returns:
    +            [dict]: [package info]
    +        """
    +        sys.path.append(package.path + "/../")  # TODO to be changed
    +        package.preinstall()
    +        for static_dir in package.static_dirs:
    +            path = package.resolve_staticdir_location(static_dir)
    +            if not j.sals.fs.exists(path):
    +                raise j.exceptions.NotFound(f"Cannot find static dir {path}")
    +
    +        # add bottle servers
    +        # we first merge all apps of a package into a single app
    +        # then mount this app on threebot main app
    +        # this will work with multiple non-standalone apps
    +        package_app = j.servers.appserver.make_main_app()
    +        for bottle_server in package.bottle_servers:
    +            path = j.sals.fs.join_paths(package.path, bottle_server["file_path"])
    +            if not j.sals.fs.exists(path):
    +                raise j.exceptions.NotFound(f"Cannot find bottle server path {path}")
    +
    +            standalone = bottle_server.get("standalone", False)
    +            if standalone:
    +                bottle_wsgi_server = package.get_bottle_server(path, bottle_server["host"], bottle_server["port"])
    +                self.threebot.rack.add(f"{package.name}_{bottle_server['name']}", bottle_wsgi_server)
    +            else:
    +                bottle_app = package.get_package_bottle_app(path)
    +                package_app.merge(bottle_app)
    +
    +        if package_app.routes:
    +            j.logger.info(f"registering {package.name} package app")
    +            self.threebot.mainapp.mount(f"/{package.name}", package_app)
    +
    +        # register gedis actors
    +        if package.actors_dir:
    +            for actor in package.actors:
    +                self.threebot.gedis._system_actor.register_actor(actor["name"], actor["path"], force_reload=True)
    +
    +        # add chatflows actors
    +        if package.chats_dir:
    +            self.threebot.chatbot.load(package.chats_dir)
    +
    +        # start background services
    +        if package.services_dir:
    +            for service in package.services:
    +                self.threebot.services.add_service(service["name"], service["path"])
    +
    +        j.logger.info(f"starting rack")
    +        # start servers
    +        self.threebot.rack.start()
    +
    +        j.logger.info(f"applying nginx config")
    +        # apply nginx configuration
    +        package.nginx_config.apply()
    +
    +        j.logger.info(f"starting package")
    +        # execute package start method
    +        package.start()
    +
    +        j.logger.info(f"reloading gedis")
    +        self.threebot.gedis_http.client.reload()
    +        j.logger.info(f"reloading nginx")
    +        self.threebot.nginx.reload()
    +
    +    def reload(self, package_name):
    +        if self.threebot.started:
    +            package = self.get(package_name)
    +            if not package:
    +                raise j.exceptions.NotFound(f"{package_name} package not found")
    +            if package.services_dir:
    +                for service in package.services:
    +                    self.threebot.services.stop_service(service["name"])
    +            self.install(package)
    +            self.threebot.nginx.reload()
    +            self.save()
    +        else:
    +            raise j.exceptions.Runtime("Can't reload package. Threebot server is not started")
    +
    +        # Return updated package info
    +        return {package.name: self.packages[package.name]}
    +
    +    def _install_all(self):
    +        """Install and apply all the packages configurations
    +        This method shall not be called directly from the shell,
    +        it must be called only from the code on the running Gedis server
    +        """
    +        all_packages = self.list_all()
    +        for package in all_packages:
    +            if package not in DEFAULT_PACKAGES:
    +                j.logger.info(f"Configuring package {package}")
    +                pkg = self.get(package)
    +                if not pkg:
    +                    j.logger.error(f"can't get package {package}")
    +                else:
    +                    if pkg.path and pkg.is_valid():
    +                        self.install(pkg)
    +                    else:
    +                        j.logger.error(f"package {package} was installed before but {pkg.path} doesn't exist anymore.")
    +
    +    def scan_packages_paths_in_dir(self, path):
    +        """Scans all packages in a path in any level and returns list of package paths
    +
    +        Args:
    +            path (str): root path that has packages on some levels
    +
    +        Returns:
    +            List[str]: list of all packages available under the path
    +        """
    +        filterfun = lambda x: str(x).endswith("package.toml")
    +        pkgtoml_paths = j.sals.fs.walk(path, filter_fun=filterfun)
    +        pkgs_paths = list(map(lambda x: x.replace("/package.toml", ""), pkgtoml_paths))
    +        return pkgs_paths
    +
    +    def scan_packages_in_dir(self, path):
    +        """Gets a dict from packages names to packages paths existing under a path that may have jumpscale packages at any level.
    +
    +        Args:
    +            path (str): root path that has packages on some levels
    +
    +        Returns:
    +            Dict[package_name, package_path]: dict of all packages available under the path
    +        """
    +        pkgname_to_path = {}
    +        for p in self.scan_packages_paths_in_dir(path):
    +            basename = j.sals.fs.basename(p).strip()
    +            if basename:
    +                pkgname_to_path[basename] = p
    +
    +        return pkgname_to_path
    +
    +
    +class ThreebotServer(Base):
    +    _package_manager = fields.Factory(PackageManager)
    +    domain = fields.String()
    +    email = fields.String()
    +    acme_server_type = fields.Enum(AcmeServer)
    +    acme_server_url = fields.URL()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._rack = None
    +        self._gedis = None
    +        self._db = None
    +        self._gedis_http = None
    +        self._services = None
    +        self._packages = None
    +        self._started = False
    +        self._nginx = None
    +        self._redis = None
    +        self.rack.add(GEDIS, self.gedis)
    +        self.rack.add(GEDIS_HTTP, self.gedis_http.gevent_server)
    +        self.rack.add(SERVICE_MANAGER, self.services)
    +
    +    def is_running(self):
    +        nginx_running = self.nginx.is_running()
    +        redis_running = self.redis.cmd.is_running() or j.sals.nettools.wait_connection_test(
    +            "127.0.0.1", 6379, timeout=1
    +        )
    +        gedis_running = j.sals.nettools.wait_connection_test("127.0.0.1", 16000, timeout=1)
    +        return nginx_running and redis_running and gedis_running
    +
    +    @property
    +    def started(self):
    +        return self._started
    +
    +    @property
    +    def nginx(self):
    +        if self._nginx is None:
    +            self._nginx = j.tools.nginx.get("default")
    +        return self._nginx
    +
    +    @property
    +    def redis(self):
    +        if self._redis is None:
    +            self._redis = j.tools.redis.get("default")
    +        return self._redis
    +
    +    @property
    +    def db(self):
    +        if self._db is None:
    +            self._db = j.core.db
    +        return self._db
    +
    +    @property
    +    def rack(self):
    +        if self._rack is None:
    +            self._rack = j.servers.rack
    +        return self._rack
    +
    +    @property
    +    def gedis(self):
    +        if self._gedis is None:
    +            self._gedis = j.servers.gedis.get("threebot")
    +        return self._gedis
    +
    +    @property
    +    def gedis_http(self):
    +        if self._gedis_http is None:
    +            self._gedis_http = j.servers.gedis_http.get("threebot")
    +        return self._gedis_http
    +
    +    @property
    +    def services(self):
    +        if self._services is None:
    +            self._services = j.tools.servicemanager.get("threebot")
    +        return self._services
    +
    +    @property
    +    def chatbot(self):
    +        return self.gedis._loaded_actors.get("chatflows_chatbot")
    +
    +    @property
    +    def packages(self):
    +        if self._packages is None:
    +            self._packages = self._package_manager.get(self.instance_name)
    +        return self._packages
    +
    +    def check_dependencies(self):
    +        install_msg = "Visit https://github.com/threefoldtech/js-sdk/blob/development/docs/wiki/quick_start.md for installation guide"
    +
    +        if not self.nginx.installed:
    +            raise j.exceptions.NotFound(f"nginx is not installed.\n{install_msg}")
    +
    +        ret = shutil.which("certbot")
    +        if not ret:
    +            raise j.exceptions.NotFound(f"certbot is not installed.\n{install_msg}")
    +
    +        rc, out, err = j.sals.process.execute("certbot plugins")
    +        if "* nginx" not in out:
    +            raise j.exceptions.NotFound(f"python-certbot-nginx is not installed.\n{install_msg}")
    +
    +        if not self.redis.installed:
    +            raise j.exceptions.NotFound(f"redis is not installed.\n{install_msg}")
    +
    +        ret = shutil.which("tmux")
    +        if not ret:
    +            raise j.exceptions.NotFound(f"tmux is not installed.\n{install_msg}")
    +
    +        ret = shutil.which("git")
    +        if not ret:
    +            raise j.exceptions.NotFound(f"git is not installed.\n{install_msg}")
    +
    +    def start(self, wait: bool = False, cert: bool = True):
    +        # start default servers in the rack
    +        # handle signals
    +        for signal_type in (signal.SIGTERM, signal.SIGINT, signal.SIGKILL):
    +            gevent.signal_handler(signal_type, self.stop)
    +
    +        # mark app as started
    +        if self.is_running():
    +            return
    +
    +        self.check_dependencies()
    +
    +        self.redis.start()
    +        self.nginx.start()
    +        j.sals.nginx.get(self.nginx.server_name).cert = cert
    +        self.mainapp = j.servers.appserver.make_main_app()
    +
    +        self.rack.start()
    +        j.logger.register(f"threebot_{self.instance_name}")
    +        # add default packages
    +        for package_name in DEFAULT_PACKAGES:
    +            j.logger.info(f"Configuring package {package_name}")
    +            try:
    +                package = self.packages.get(package_name)
    +                self.packages.install(package)
    +            except Exception as e:
    +                self.stop()
    +                raise j.core.exceptions.Runtime(
    +                    f"Error happened during getting or installing {package_name} package, the detailed error is {str(e)}"
    +                ) from e
    +
    +        # install all package
    +
    +        j.logger.info("Adding packages")
    +        self.packages._install_all()
    +        j.logger.info("jsappserver")
    +        self.jsappserver = WSGIServer(("localhost", 31000), apply_main_middlewares(self.mainapp))
    +        j.logger.info("rack add")
    +        self.rack.add(f"appserver", self.jsappserver)
    +
    +        j.logger.info("Reloading nginx")
    +        self.nginx.reload()
    +
    +        # mark server as started
    +        self._started = True
    +        j.logger.info(f"routes: {self.mainapp.routes}")
    +        j.logger.info(f"Threebot is running at http://localhost:{PORTS.HTTP} and https://localhost:{PORTS.HTTPS}")
    +        self.rack.start(wait=wait)  # to keep the server running
    +
    +    def stop(self):
    +        server_packages = self.packages.list_all()
    +        for package_name in server_packages:
    +            package = self.packages.get(package_name)
    +            package.stop()
    +        self.nginx.stop()
    +        # mark app as stopped, do this before stopping redis
    +        j.logger.unregister()
    +        self.rack.stop()
    +        self.redis.stop()
    +        self._started = False
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class NginxPackageConfig +(package) +
    +
    +
    +
    + +Expand source code + +
    class NginxPackageConfig:
    +    def __init__(self, package):
    +        self.package = package
    +        self.nginx = j.sals.nginx.get("main")
    +
    +    @property
    +    def default_config(self):
    +        default_server = {
    +            "name": "default",
    +            "ports": self.package.config.get("ports"),
    +            "locations": self.package.config.get("locations", []),
    +            "domain": self.package.default_domain,
    +            "letsencryptemail": self.package.default_email,
    +            "acme_server_type": self.package.default_acme_server_type,
    +            "acme_server_url": self.package.default_acme_server_url,
    +        }
    +
    +        is_auth = self.package.config.get("is_auth", True)
    +        is_admin = self.package.config.get("is_admin", True)
    +        is_package_authorized = self.package.config.get("is_package_authorized", False)
    +
    +        for static_dir in self.package.static_dirs:
    +            path_url = j.data.text.removeprefix(static_dir.get("path_url"), "/")
    +            default_server["locations"].append(
    +                {
    +                    "is_auth": static_dir.get("is_auth", is_auth),
    +                    "is_admin": static_dir.get("is_admin", is_admin),
    +                    "is_package_authorized": static_dir.get("is_package_authorized", is_package_authorized),
    +                    "type": "static",
    +                    "name": static_dir.get("name"),
    +                    "spa": static_dir.get("spa"),
    +                    "index": static_dir.get("index"),
    +                    "path_url": j.sals.fs.join_paths(self.package.base_url, path_url),
    +                    "path_location": self.package.resolve_staticdir_location(static_dir),
    +                    "force_https": self.package.config.get("force_https", True),
    +                }
    +            )
    +
    +        for bottle_server in self.package.bottle_servers:
    +            path_url = j.data.text.removeprefix(bottle_server.get("path_url"), "/")
    +            if hasattr(bottle_server, "standalone") and bottle_server.standalone:
    +                default_server["locations"].append(
    +                    {
    +                        "is_auth": bottle_server.get("is_auth", is_auth),
    +                        "is_admin": bottle_server.get("is_admin", is_admin),
    +                        "is_package_authorized": bottle_server.get("is_package_authorized", is_package_authorized),
    +                        "type": "proxy",
    +                        "name": bottle_server.get("name"),
    +                        "host": bottle_server.get("host"),
    +                        "port": bottle_server.get("port"),
    +                        "path_url": j.sals.fs.join_paths(self.package.base_url,),
    +                        "path_dest": bottle_server.get("path_dest"),
    +                        "websocket": bottle_server.get("websocket"),
    +                        "force_https": self.package.config.get("force_https", True),
    +                    }
    +                )
    +            else:
    +                path_url = j.data.text.removeprefix(bottle_server.get("path_url"), "/")
    +                default_server["locations"].append(
    +                    {
    +                        "is_auth": bottle_server.get("is_auth", is_auth),
    +                        "is_admin": bottle_server.get("is_admin", is_admin),
    +                        "is_package_authorized": bottle_server.get("is_package_authorized", is_package_authorized),
    +                        "type": "proxy",
    +                        "name": bottle_server.get("name"),
    +                        "host": "0.0.0.0",
    +                        "port": 31000,
    +                        "path_url": j.sals.fs.join_paths(self.package.base_url, path_url),
    +                        "path_dest": f"/{self.package.name}{bottle_server.get('path_dest')}",
    +                        "websocket": bottle_server.get("websocket"),
    +                        "force_https": self.package.config.get("force_https", True),
    +                    }
    +                )
    +
    +        if self.package.actors_dir:
    +            default_server["locations"].append(
    +                {
    +                    "is_auth": is_auth,
    +                    "is_admin": is_admin,
    +                    "is_package_authorized": is_package_authorized,
    +                    "type": "proxy",
    +                    "name": "actors",
    +                    "host": GEDIS_HTTP_HOST,
    +                    "port": GEDIS_HTTP_PORT,
    +                    "path_url": j.sals.fs.join_paths(self.package.base_url, "actors"),
    +                    "path_dest": self.package.base_url,
    +                    "force_https": self.package.config.get("force_https", True),
    +                }
    +            )
    +
    +        if self.package.chats_dir:
    +            default_server["locations"].append(
    +                {
    +                    "is_auth": is_auth,
    +                    "is_admin": is_admin,
    +                    "is_package_authorized": is_package_authorized,
    +                    "type": "proxy",
    +                    "name": "chats",
    +                    "host": CHATFLOW_SERVER_HOST,
    +                    "port": CHATFLOW_SERVER_PORT,
    +                    "path_url": j.sals.fs.join_paths(self.package.base_url, "chats"),
    +                    "path_dest": f"/chatflows{self.package.base_url}/chats",  # TODO: temperoary fix for auth package
    +                    "force_https": self.package.config.get("force_https", True),
    +                }
    +            )
    +
    +        return [default_server]
    +
    +    def apply(self, write_config=True):
    +        default_ports = [PORTS.HTTP, PORTS.HTTPS]
    +        servers = self.default_config + self.package.config.get("servers", [])
    +        for server in servers:
    +            ports = server.get("ports", default_ports) or default_ports
    +            for port in ports:
    +                server_name = server.get("name")
    +                if server_name != "default":
    +                    server_name = f"{self.package.name}_{server_name}"
    +
    +                website = self.nginx.get_website(server_name, port=port)
    +                website.ssl = server.get("ssl", port == PORTS.HTTPS)
    +                website.includes = server.get("includes", [])
    +                website.domain = server.get("domain", self.default_config[0].get("domain"))
    +                website.letsencryptemail = server.get(
    +                    "letsencryptemail", self.default_config[0].get("letsencryptemail")
    +                )
    +                website.acme_server_type = server.get(
    +                    "acme_server_type", self.default_config[0].get("acme_server_type")
    +                )
    +                website.acme_server_url = server.get("acme_server_url", self.default_config[0].get("acme_server_url"))
    +                if server.get("key_path"):
    +                    website.key_path = server["key_path"]
    +                if server.get("cert_path"):
    +                    website.cert_path = server["cert_path"]
    +                if server.get("fullchain_path"):
    +                    website.fullchain_path = server["fullchain_path"]
    +
    +                for location in server.get("locations", []):
    +                    loc = None
    +
    +                    location_name = location.get("name")
    +                    location_name = f"{self.package.name}_{location_name}"
    +                    location_type = location.get("type", "static")
    +
    +                    if location_type == "static":
    +                        loc = website.get_static_location(location_name)
    +                        loc.spa = location.get("spa", False)
    +                        loc.index = location.get("index")
    +                        loc.path_location = location.get("path_location")
    +
    +                    elif location_type == "proxy":
    +                        loc = website.get_proxy_location(location_name)
    +                        loc.scheme = location.get("scheme", "http")
    +                        loc.host = location.get("host")
    +                        loc.port = location.get("port", PORTS.HTTP)
    +                        loc.path_dest = location.get("path_dest", "")
    +                        loc.websocket = location.get("websocket", False)
    +                        loc.proxy_buffering = location.get("proxy_buffering", "")
    +                        loc.proxy_buffers = location.get("proxy_buffers")
    +                        loc.proxy_buffer_size = location.get("proxy_buffer_size")
    +
    +                    elif location_type == "custom":
    +                        loc = website.get_custom_location(location_name)
    +                        loc.custom_config = location.get("custom_config")
    +
    +                    if loc:
    +                        loc.location_type = location_type
    +                        path_url = location.get("path_url", "/")
    +                        if loc.location_type == LocationType.PROXY:
    +                            # proxy location needs / (as we append slash to the backend server too)
    +                            # and nginx always redirects to the same location with slash
    +                            # this way, requests will go to backend servers without double slashes...etc
    +                            if not path_url.endswith("/"):
    +                                path_url += "/"
    +                        else:
    +                            # for other locations, slash is not required
    +                            if path_url != "/":
    +                                path_url = path_url.rstrip("/")
    +
    +                        loc.path_url = path_url
    +                        loc.force_https = location.get("force_https")
    +                        loc.is_auth = location.get("is_auth", False)
    +                        loc.is_admin = location.get("is_admin", False)
    +                        loc.is_package_authorized = location.get("is_package_authorized", False)
    +                        loc.package_name = self.package.name
    +                if write_config:
    +                    website.configure(generate_certificates=self.nginx.cert)
    +
    +

    Instance variables

    +
    +
    var default_config
    +
    +
    +
    + +Expand source code + +
    @property
    +def default_config(self):
    +    default_server = {
    +        "name": "default",
    +        "ports": self.package.config.get("ports"),
    +        "locations": self.package.config.get("locations", []),
    +        "domain": self.package.default_domain,
    +        "letsencryptemail": self.package.default_email,
    +        "acme_server_type": self.package.default_acme_server_type,
    +        "acme_server_url": self.package.default_acme_server_url,
    +    }
    +
    +    is_auth = self.package.config.get("is_auth", True)
    +    is_admin = self.package.config.get("is_admin", True)
    +    is_package_authorized = self.package.config.get("is_package_authorized", False)
    +
    +    for static_dir in self.package.static_dirs:
    +        path_url = j.data.text.removeprefix(static_dir.get("path_url"), "/")
    +        default_server["locations"].append(
    +            {
    +                "is_auth": static_dir.get("is_auth", is_auth),
    +                "is_admin": static_dir.get("is_admin", is_admin),
    +                "is_package_authorized": static_dir.get("is_package_authorized", is_package_authorized),
    +                "type": "static",
    +                "name": static_dir.get("name"),
    +                "spa": static_dir.get("spa"),
    +                "index": static_dir.get("index"),
    +                "path_url": j.sals.fs.join_paths(self.package.base_url, path_url),
    +                "path_location": self.package.resolve_staticdir_location(static_dir),
    +                "force_https": self.package.config.get("force_https", True),
    +            }
    +        )
    +
    +    for bottle_server in self.package.bottle_servers:
    +        path_url = j.data.text.removeprefix(bottle_server.get("path_url"), "/")
    +        if hasattr(bottle_server, "standalone") and bottle_server.standalone:
    +            default_server["locations"].append(
    +                {
    +                    "is_auth": bottle_server.get("is_auth", is_auth),
    +                    "is_admin": bottle_server.get("is_admin", is_admin),
    +                    "is_package_authorized": bottle_server.get("is_package_authorized", is_package_authorized),
    +                    "type": "proxy",
    +                    "name": bottle_server.get("name"),
    +                    "host": bottle_server.get("host"),
    +                    "port": bottle_server.get("port"),
    +                    "path_url": j.sals.fs.join_paths(self.package.base_url,),
    +                    "path_dest": bottle_server.get("path_dest"),
    +                    "websocket": bottle_server.get("websocket"),
    +                    "force_https": self.package.config.get("force_https", True),
    +                }
    +            )
    +        else:
    +            path_url = j.data.text.removeprefix(bottle_server.get("path_url"), "/")
    +            default_server["locations"].append(
    +                {
    +                    "is_auth": bottle_server.get("is_auth", is_auth),
    +                    "is_admin": bottle_server.get("is_admin", is_admin),
    +                    "is_package_authorized": bottle_server.get("is_package_authorized", is_package_authorized),
    +                    "type": "proxy",
    +                    "name": bottle_server.get("name"),
    +                    "host": "0.0.0.0",
    +                    "port": 31000,
    +                    "path_url": j.sals.fs.join_paths(self.package.base_url, path_url),
    +                    "path_dest": f"/{self.package.name}{bottle_server.get('path_dest')}",
    +                    "websocket": bottle_server.get("websocket"),
    +                    "force_https": self.package.config.get("force_https", True),
    +                }
    +            )
    +
    +    if self.package.actors_dir:
    +        default_server["locations"].append(
    +            {
    +                "is_auth": is_auth,
    +                "is_admin": is_admin,
    +                "is_package_authorized": is_package_authorized,
    +                "type": "proxy",
    +                "name": "actors",
    +                "host": GEDIS_HTTP_HOST,
    +                "port": GEDIS_HTTP_PORT,
    +                "path_url": j.sals.fs.join_paths(self.package.base_url, "actors"),
    +                "path_dest": self.package.base_url,
    +                "force_https": self.package.config.get("force_https", True),
    +            }
    +        )
    +
    +    if self.package.chats_dir:
    +        default_server["locations"].append(
    +            {
    +                "is_auth": is_auth,
    +                "is_admin": is_admin,
    +                "is_package_authorized": is_package_authorized,
    +                "type": "proxy",
    +                "name": "chats",
    +                "host": CHATFLOW_SERVER_HOST,
    +                "port": CHATFLOW_SERVER_PORT,
    +                "path_url": j.sals.fs.join_paths(self.package.base_url, "chats"),
    +                "path_dest": f"/chatflows{self.package.base_url}/chats",  # TODO: temperoary fix for auth package
    +                "force_https": self.package.config.get("force_https", True),
    +            }
    +        )
    +
    +    return [default_server]
    +
    +
    +
    +

    Methods

    +
    +
    +def apply(self, write_config=True) +
    +
    +
    +
    + +Expand source code + +
    def apply(self, write_config=True):
    +    default_ports = [PORTS.HTTP, PORTS.HTTPS]
    +    servers = self.default_config + self.package.config.get("servers", [])
    +    for server in servers:
    +        ports = server.get("ports", default_ports) or default_ports
    +        for port in ports:
    +            server_name = server.get("name")
    +            if server_name != "default":
    +                server_name = f"{self.package.name}_{server_name}"
    +
    +            website = self.nginx.get_website(server_name, port=port)
    +            website.ssl = server.get("ssl", port == PORTS.HTTPS)
    +            website.includes = server.get("includes", [])
    +            website.domain = server.get("domain", self.default_config[0].get("domain"))
    +            website.letsencryptemail = server.get(
    +                "letsencryptemail", self.default_config[0].get("letsencryptemail")
    +            )
    +            website.acme_server_type = server.get(
    +                "acme_server_type", self.default_config[0].get("acme_server_type")
    +            )
    +            website.acme_server_url = server.get("acme_server_url", self.default_config[0].get("acme_server_url"))
    +            if server.get("key_path"):
    +                website.key_path = server["key_path"]
    +            if server.get("cert_path"):
    +                website.cert_path = server["cert_path"]
    +            if server.get("fullchain_path"):
    +                website.fullchain_path = server["fullchain_path"]
    +
    +            for location in server.get("locations", []):
    +                loc = None
    +
    +                location_name = location.get("name")
    +                location_name = f"{self.package.name}_{location_name}"
    +                location_type = location.get("type", "static")
    +
    +                if location_type == "static":
    +                    loc = website.get_static_location(location_name)
    +                    loc.spa = location.get("spa", False)
    +                    loc.index = location.get("index")
    +                    loc.path_location = location.get("path_location")
    +
    +                elif location_type == "proxy":
    +                    loc = website.get_proxy_location(location_name)
    +                    loc.scheme = location.get("scheme", "http")
    +                    loc.host = location.get("host")
    +                    loc.port = location.get("port", PORTS.HTTP)
    +                    loc.path_dest = location.get("path_dest", "")
    +                    loc.websocket = location.get("websocket", False)
    +                    loc.proxy_buffering = location.get("proxy_buffering", "")
    +                    loc.proxy_buffers = location.get("proxy_buffers")
    +                    loc.proxy_buffer_size = location.get("proxy_buffer_size")
    +
    +                elif location_type == "custom":
    +                    loc = website.get_custom_location(location_name)
    +                    loc.custom_config = location.get("custom_config")
    +
    +                if loc:
    +                    loc.location_type = location_type
    +                    path_url = location.get("path_url", "/")
    +                    if loc.location_type == LocationType.PROXY:
    +                        # proxy location needs / (as we append slash to the backend server too)
    +                        # and nginx always redirects to the same location with slash
    +                        # this way, requests will go to backend servers without double slashes...etc
    +                        if not path_url.endswith("/"):
    +                            path_url += "/"
    +                    else:
    +                        # for other locations, slash is not required
    +                        if path_url != "/":
    +                            path_url = path_url.rstrip("/")
    +
    +                    loc.path_url = path_url
    +                    loc.force_https = location.get("force_https")
    +                    loc.is_auth = location.get("is_auth", False)
    +                    loc.is_admin = location.get("is_admin", False)
    +                    loc.is_package_authorized = location.get("is_package_authorized", False)
    +                    loc.package_name = self.package.name
    +            if write_config:
    +                website.configure(generate_certificates=self.nginx.cert)
    +
    +
    +
    +
    +
    +class Package +(path, default_domain, default_email, giturl='', kwargs=None, admins=None, default_acme_server_type=AcmeServer.LETSENCRYPT, default_acme_server_url=None) +
    +
    +
    +
    + +Expand source code + +
    class Package:
    +    def __init__(
    +        self,
    +        path,
    +        default_domain,
    +        default_email,
    +        giturl="",
    +        kwargs=None,
    +        admins=None,
    +        default_acme_server_type=AcmeServer.LETSENCRYPT,
    +        default_acme_server_url=None,
    +    ):
    +        self.path = path
    +        self.giturl = giturl
    +        self._config = None
    +        self.name = j.sals.fs.basename(path.rstrip("/"))
    +        self.nginx_config = NginxPackageConfig(self)
    +        self._module = None
    +        self.default_domain = default_domain
    +        self.default_email = default_email
    +        self.default_acme_server_type = default_acme_server_type
    +        self.default_acme_server_url = default_acme_server_url
    +        self.kwargs = kwargs or {}
    +        self.admins = admins or []
    +
    +    def _load_files(self, dir_path):
    +        for file_path in j.sals.fs.walk_files(dir_path, recursive=False):
    +            file_name = j.sals.fs.basename(file_path)
    +            if file_name.endswith(".py"):
    +                name = f"{self.name}_{file_name[:-3]}"
    +                yield dict(name=name, path=file_path)
    +
    +    def load_config(self):
    +        return toml.load(self.package_config_path)
    +
    +    @property
    +    def package_config_path(self):
    +        return j.sals.fs.join_paths(self.path, "package.toml")
    +
    +    @property
    +    def package_module_path(self):
    +        return j.sals.fs.join_paths(self.path, "package.py")
    +
    +    @property
    +    def module(self):
    +        if self._module is None:
    +            package_file_path = j.sals.fs.join_paths(self.path, "package.py")
    +            if j.sals.fs.exists(package_file_path):
    +                module = imp.load_source(self.name, package_file_path)
    +                if not hasattr(module, self.name):
    +                    raise j.exceptions.Halt(f"missing class ({self.name}) in the package file")
    +
    +                self._module = getattr(module, self.name)()
    +        return self._module
    +
    +    @property
    +    def base_url(self):
    +        return j.sals.fs.join_paths("/", self.name)
    +
    +    @property
    +    def config(self):
    +        if not self._config:
    +            self._config = self.load_config()
    +        return self._config
    +
    +    @property
    +    def ui_name(self):
    +        return self.config.get("ui_name", self.name)
    +
    +    @property
    +    def actors_dir(self):
    +        actors_dir = j.sals.fs.join_paths(self.path, self.config.get("actors_dir", "actors"))
    +        if j.sals.fs.exists(actors_dir):
    +            return actors_dir
    +
    +    @property
    +    def chats_dir(self):
    +        chats_dir = j.sals.fs.join_paths(self.path, self.config.get("chats_dir", "chats"))
    +        if j.sals.fs.exists(chats_dir):
    +            return chats_dir
    +
    +    @property
    +    def services_dir(self):
    +        services_dir = j.sals.fs.join_paths(self.path, self.config.get("services_dir", "services"))
    +        if j.sals.fs.exists(services_dir):
    +            return services_dir
    +
    +    @property
    +    def static_dirs(self):
    +        return self.config.get("static_dirs", [])
    +
    +    @property
    +    def bottle_servers(self):
    +        return self.config.get("bottle_servers", [])
    +
    +    @property
    +    def actors(self):
    +        return self._load_files(self.actors_dir)
    +
    +    @property
    +    def services(self):
    +        return self._load_files(self.services_dir)
    +
    +    def resolve_staticdir_location(self, static_dir):
    +        """Resolves path for static location in case we need it
    +        absoulute or not
    +
    +        static_dir.absolute_path true it will return the path directly
    +        if false will be relative to the path
    +
    +        Args:
    +            static_dir (str): package.toml static dirs category
    +
    +        Returns:
    +            str: package path
    +        """
    +        path_location = static_dir.get("path_location")
    +        absolute_path = static_dir.get("absolute_path", False)
    +        if absolute_path:
    +            return j.sals.fs.expanduser(path_location)
    +        return j.sals.fs.expanduser(j.sals.fs.join_paths(self.path, path_location))
    +
    +    def get_bottle_server(self, file_path, host, port):
    +        module = imp.load_source(file_path[:-3], file_path)
    +        return WSGIServer((host, port), StripPathMiddleware(module.app))
    +
    +    def get_package_bottle_app(self, file_path):
    +        module = imp.load_source(file_path[:-3], file_path)
    +        return module.app
    +
    +    def preinstall(self):
    +        if self.module and hasattr(self.module, "preinstall"):
    +            self.module.preinstall()
    +
    +    def install(self, **kwargs):
    +        if self.module and hasattr(self.module, "install"):
    +            self.module.install(**kwargs)
    +
    +    def uninstall(self):
    +        if self.module and hasattr(self.module, "uninstall"):
    +            self.module.uninstall()
    +
    +    def start(self):
    +        if self.module and hasattr(self.module, "start"):
    +            self.module.start(**self.kwargs)
    +
    +    def stop(self):
    +        if self.module and hasattr(self.module, "stop"):
    +            self.module.stop()
    +
    +    def restart(self):
    +        if self.module:
    +            self.module.stop()
    +            self.module.start()
    +
    +    def exists(self):
    +        return j.sals.fs.exists(self.package_config_path)
    +
    +    def is_valid(self):
    +        # more constraints, but for now let's say it's not ok if the main files don't exist
    +        return self.exists() and not self.is_excluded()
    +
    +    def is_excluded(self):
    +        return self.config.get("excluded", False) == True
    +
    +

    Instance variables

    +
    +
    var actors
    +
    +
    +
    + +Expand source code + +
    @property
    +def actors(self):
    +    return self._load_files(self.actors_dir)
    +
    +
    +
    var actors_dir
    +
    +
    +
    + +Expand source code + +
    @property
    +def actors_dir(self):
    +    actors_dir = j.sals.fs.join_paths(self.path, self.config.get("actors_dir", "actors"))
    +    if j.sals.fs.exists(actors_dir):
    +        return actors_dir
    +
    +
    +
    var base_url
    +
    +
    +
    + +Expand source code + +
    @property
    +def base_url(self):
    +    return j.sals.fs.join_paths("/", self.name)
    +
    +
    +
    var bottle_servers
    +
    +
    +
    + +Expand source code + +
    @property
    +def bottle_servers(self):
    +    return self.config.get("bottle_servers", [])
    +
    +
    +
    var chats_dir
    +
    +
    +
    + +Expand source code + +
    @property
    +def chats_dir(self):
    +    chats_dir = j.sals.fs.join_paths(self.path, self.config.get("chats_dir", "chats"))
    +    if j.sals.fs.exists(chats_dir):
    +        return chats_dir
    +
    +
    +
    var config
    +
    +
    +
    + +Expand source code + +
    @property
    +def config(self):
    +    if not self._config:
    +        self._config = self.load_config()
    +    return self._config
    +
    +
    +
    var module
    +
    +
    +
    + +Expand source code + +
    @property
    +def module(self):
    +    if self._module is None:
    +        package_file_path = j.sals.fs.join_paths(self.path, "package.py")
    +        if j.sals.fs.exists(package_file_path):
    +            module = imp.load_source(self.name, package_file_path)
    +            if not hasattr(module, self.name):
    +                raise j.exceptions.Halt(f"missing class ({self.name}) in the package file")
    +
    +            self._module = getattr(module, self.name)()
    +    return self._module
    +
    +
    +
    var package_config_path
    +
    +
    +
    + +Expand source code + +
    @property
    +def package_config_path(self):
    +    return j.sals.fs.join_paths(self.path, "package.toml")
    +
    +
    +
    var package_module_path
    +
    +
    +
    + +Expand source code + +
    @property
    +def package_module_path(self):
    +    return j.sals.fs.join_paths(self.path, "package.py")
    +
    +
    +
    var services
    +
    +
    +
    + +Expand source code + +
    @property
    +def services(self):
    +    return self._load_files(self.services_dir)
    +
    +
    +
    var services_dir
    +
    +
    +
    + +Expand source code + +
    @property
    +def services_dir(self):
    +    services_dir = j.sals.fs.join_paths(self.path, self.config.get("services_dir", "services"))
    +    if j.sals.fs.exists(services_dir):
    +        return services_dir
    +
    +
    +
    var static_dirs
    +
    +
    +
    + +Expand source code + +
    @property
    +def static_dirs(self):
    +    return self.config.get("static_dirs", [])
    +
    +
    +
    var ui_name
    +
    +
    +
    + +Expand source code + +
    @property
    +def ui_name(self):
    +    return self.config.get("ui_name", self.name)
    +
    +
    +
    +

    Methods

    +
    +
    +def exists(self) +
    +
    +
    +
    + +Expand source code + +
    def exists(self):
    +    return j.sals.fs.exists(self.package_config_path)
    +
    +
    +
    +def get_bottle_server(self, file_path, host, port) +
    +
    +
    +
    + +Expand source code + +
    def get_bottle_server(self, file_path, host, port):
    +    module = imp.load_source(file_path[:-3], file_path)
    +    return WSGIServer((host, port), StripPathMiddleware(module.app))
    +
    +
    +
    +def get_package_bottle_app(self, file_path) +
    +
    +
    +
    + +Expand source code + +
    def get_package_bottle_app(self, file_path):
    +    module = imp.load_source(file_path[:-3], file_path)
    +    return module.app
    +
    +
    +
    +def install(self, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def install(self, **kwargs):
    +    if self.module and hasattr(self.module, "install"):
    +        self.module.install(**kwargs)
    +
    +
    +
    +def is_excluded(self) +
    +
    +
    +
    + +Expand source code + +
    def is_excluded(self):
    +    return self.config.get("excluded", False) == True
    +
    +
    +
    +def is_valid(self) +
    +
    +
    +
    + +Expand source code + +
    def is_valid(self):
    +    # more constraints, but for now let's say it's not ok if the main files don't exist
    +    return self.exists() and not self.is_excluded()
    +
    +
    +
    +def load_config(self) +
    +
    +
    +
    + +Expand source code + +
    def load_config(self):
    +    return toml.load(self.package_config_path)
    +
    +
    +
    +def preinstall(self) +
    +
    +
    +
    + +Expand source code + +
    def preinstall(self):
    +    if self.module and hasattr(self.module, "preinstall"):
    +        self.module.preinstall()
    +
    +
    +
    +def resolve_staticdir_location(self, static_dir) +
    +
    +

    Resolves path for static location in case we need it +absoulute or not

    +

    static_dir.absolute_path true it will return the path directly +if false will be relative to the path

    +

    Args

    +
    +
    static_dir : str
    +
    package.toml static dirs category
    +
    +

    Returns

    +
    +
    str
    +
    package path
    +
    +
    + +Expand source code + +
    def resolve_staticdir_location(self, static_dir):
    +    """Resolves path for static location in case we need it
    +    absoulute or not
    +
    +    static_dir.absolute_path true it will return the path directly
    +    if false will be relative to the path
    +
    +    Args:
    +        static_dir (str): package.toml static dirs category
    +
    +    Returns:
    +        str: package path
    +    """
    +    path_location = static_dir.get("path_location")
    +    absolute_path = static_dir.get("absolute_path", False)
    +    if absolute_path:
    +        return j.sals.fs.expanduser(path_location)
    +    return j.sals.fs.expanduser(j.sals.fs.join_paths(self.path, path_location))
    +
    +
    +
    +def restart(self) +
    +
    +
    +
    + +Expand source code + +
    def restart(self):
    +    if self.module:
    +        self.module.stop()
    +        self.module.start()
    +
    +
    +
    +def start(self) +
    +
    +
    +
    + +Expand source code + +
    def start(self):
    +    if self.module and hasattr(self.module, "start"):
    +        self.module.start(**self.kwargs)
    +
    +
    +
    +def stop(self) +
    +
    +
    +
    + +Expand source code + +
    def stop(self):
    +    if self.module and hasattr(self.module, "stop"):
    +        self.module.stop()
    +
    +
    +
    +def uninstall(self) +
    +
    +
    +
    + +Expand source code + +
    def uninstall(self):
    +    if self.module and hasattr(self.module, "uninstall"):
    +        self.module.uninstall()
    +
    +
    +
    +
    +
    +class PackageManager +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class PackageManager(Base):
    +    packages = fields.Typed(dict, default=DEFAULT_PACKAGES.copy())
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._threebot = None
    +
    +    @property
    +    def threebot(self):
    +        if self._threebot is None:
    +            self._threebot = j.servers.threebot.get()
    +        return self._threebot
    +
    +    def get(self, package_name):
    +        if package_name in self.packages:
    +            package_path = self.packages[package_name]["path"]
    +            package_giturl = self.packages[package_name]["giturl"]
    +            package_kwargs = self.packages[package_name].get("kwargs", {})
    +            package_admins = self.packages[package_name].get("admins", [])
    +
    +            return Package(
    +                path=package_path,
    +                default_domain=self.threebot.domain,
    +                default_email=self.threebot.email,
    +                default_acme_server_type=self.threebot.acme_server_type,
    +                default_acme_server_url=self.threebot.acme_server_url,
    +                giturl=package_giturl,
    +                kwargs=package_kwargs,
    +                admins=package_admins,
    +            )
    +
    +    def get_packages(self):
    +        all_packages = []
    +
    +        # Add installed packages including outer packages
    +        for pkg in self.packages:
    +            package = self.get(pkg)
    +
    +            if package and package.is_valid():
    +                if j.sals.fs.exists(package.path):
    +                    chatflows = True if package.chats_dir else False
    +                    all_packages.append(
    +                        {
    +                            "name": pkg,
    +                            "path": package.path,
    +                            "giturl": package.giturl,
    +                            "system_package": pkg in DEFAULT_PACKAGES.keys(),
    +                            "installed": True,
    +                            "frontend": package.config.get("frontend", False),
    +                            "chatflows": chatflows,
    +                            "ui_name": package.ui_name,
    +                        }
    +                    )
    +                else:
    +                    j.logger.error(f"path {package.path} for {pkg} doesn't exist anymore")
    +            else:
    +                j.logger.error("pkg {pkg} is in self.packages but it's None")
    +
    +        # Add uninstalled sdk packages under j.packages
    +        for path in set(pkgnamespace.__path__):
    +            for pkg in os.listdir(path):
    +                pkg_path = j.sals.fs.join_paths(path, pkg)
    +                pkgtoml_path = j.sals.fs.join_paths(pkg_path, "package.toml")
    +                ui_name = pkg
    +                excluded = False
    +                with open(pkgtoml_path) as f:
    +                    conf = j.data.serializers.toml.loads(f.read())
    +                    ui_name = conf.get("ui_name", pkg)
    +                    excluded = conf.get("excluded", False)
    +                if pkg not in self.packages and j.sals.fs.exists(pkgtoml_path) and not excluded:
    +                    all_packages.append(
    +                        {
    +                            "name": pkg,
    +                            "path": j.sals.fs.dirname(getattr(j.packages, pkg).__file__),
    +                            "giturl": "",
    +                            "system_package": pkg in DEFAULT_PACKAGES.keys(),
    +                            "installed": False,
    +                            "ui_name": ui_name,
    +                        }
    +                    )
    +
    +        return all_packages
    +
    +    def list_all(self):
    +        return list(self.packages.keys())
    +
    +    def add(self, path: str = None, giturl: str = None, **kwargs):
    +        # first check if public repo
    +        # TODO: Check if package already exists
    +        if not any([path, giturl]) or all([path, giturl]):
    +            raise j.exceptions.Value("either path or giturl is required")
    +        pkg_name = ""
    +        if giturl:
    +            url = urlparse(giturl)
    +            url_parts = url.path.lstrip("/").split("/")
    +            if len(url_parts) == 2:
    +                pkg_name = url_parts[1].strip("/")
    +                j.logger.debug(
    +                    f"user didn't pass a URL containing branch {giturl}, try to guess (master, main, development) in order"
    +                )
    +                if j.tools.http.get(f"{giturl}/tree/master").status_code == 200:
    +                    url_parts.extend(["tree", "master"])
    +                elif j.tools.http.get(f"{giturl}/tree/main").status_code == 200:
    +                    url_parts.extend(["tree", "main"])
    +                elif j.tools.http.get(f"{giturl}/tree/development").status_code == 200:
    +                    url_parts.extend(["tree", "development"])
    +                else:
    +                    raise j.exceptions.Value(f"couldn't guess the branch for {giturl}")
    +            else:
    +                pkg_name = url_parts[-1].strip("/")
    +
    +            if len(url_parts) < 4:
    +                raise j.exceptions.Value(f"invalid git URL {giturl}")
    +
    +            org, repo, _, branch = url_parts[:4]
    +            repo_dir = f"{org}_{repo}_{pkg_name}_{branch}"
    +            repo_path = j.sals.fs.join_paths(DOWNLOADED_PACKAGES_PATH, repo_dir)
    +            repo_url = f"{url.scheme}://{url.hostname}/{org}/{repo}"
    +
    +            # delete repo dir if exists
    +            j.sals.fs.rmtree(repo_path)
    +
    +            j.tools.git.clone_repo(url=repo_url, dest=repo_path, branch_or_tag=branch)
    +            toml_paths = list(
    +                j.sals.fs.walk(repo_path, "*", filter_fun=lambda x: str(x).endswith(f"{pkg_name}/package.toml"))
    +            )
    +            if not toml_paths:
    +                raise j.exceptions.Value(f"couldn't find {pkg_name}/package.toml in {repo_path}")
    +            path_for_package_toml = toml_paths[0]
    +            package_path = j.sals.fs.parent(path_for_package_toml)
    +            path = package_path
    +
    +        admins = kwargs.pop("admins", [])
    +
    +        package = Package(
    +            path=path,
    +            default_domain=self.threebot.domain,
    +            default_email=self.threebot.email,
    +            giturl=giturl,
    +            kwargs=kwargs,
    +            admins=admins,
    +        )
    +
    +        # TODO: adding under the same name if same path and same giturl should be fine, no?
    +        # if package.name in self.packages:
    +        #     raise j.exceptions.Value(f"Package with name {package.name} already exists")
    +
    +        # execute package install method
    +        package.install(**kwargs)
    +
    +        # install package if threebot is started
    +        if self.threebot.started:
    +            self.install(package)
    +            self.threebot.nginx.reload()
    +        self.packages[package.name] = {
    +            "name": package.name,
    +            "path": package.path,
    +            "giturl": package.giturl,
    +            "kwargs": package.kwargs,
    +            "admins": package.admins,
    +            "ui_name": package.ui_name,
    +        }
    +
    +        self.save()
    +
    +        # Return updated package info
    +        return {package.name: self.packages[package.name]}
    +
    +    def delete(self, package_name):
    +        if package_name in DEFAULT_PACKAGES:
    +            raise j.exceptions.Value("cannot delete default packages")
    +        package = self.get(package_name)
    +        if not package:
    +            raise j.exceptions.NotFound(f"{package_name} package not found")
    +
    +        # remove bottle servers
    +        rack_servers = list(self.threebot.rack._servers)
    +        for bottle_server in rack_servers:
    +            if bottle_server.startswith(f"{package_name}_"):
    +                self.threebot.rack.remove(bottle_server)
    +
    +        # stop background services
    +        if package.services_dir:
    +            for service in package.services:
    +                self.threebot.services.stop_service(service["name"])
    +
    +        if self.threebot.started:
    +            # unregister gedis actors
    +            gedis_actors = list(self.threebot.gedis._loaded_actors.keys())
    +            for actor in gedis_actors:
    +                if actor.startswith(f"{package_name}_"):
    +                    self.threebot.gedis._system_actor.unregister_actor(actor)
    +
    +            # unload chats
    +            try:
    +                if package.chats_dir:
    +                    self.threebot.chatbot.unload(package.chats_dir)
    +            except Exception as e:
    +                j.logger.warning(f"Couldn't unload the chats of package {package_name}, this is the exception {str(e)}")
    +
    +            # reload nginx
    +            self.threebot.nginx.reload()
    +
    +        # execute package uninstall method
    +        package.uninstall()
    +
    +        self.packages.pop(package_name)
    +        self.save()
    +
    +    def install(self, package):
    +        """install and apply package configrations
    +
    +        Args:
    +            package ([package object]): get package object using [self.get(package_name)]
    +
    +        Returns:
    +            [dict]: [package info]
    +        """
    +        sys.path.append(package.path + "/../")  # TODO to be changed
    +        package.preinstall()
    +        for static_dir in package.static_dirs:
    +            path = package.resolve_staticdir_location(static_dir)
    +            if not j.sals.fs.exists(path):
    +                raise j.exceptions.NotFound(f"Cannot find static dir {path}")
    +
    +        # add bottle servers
    +        # we first merge all apps of a package into a single app
    +        # then mount this app on threebot main app
    +        # this will work with multiple non-standalone apps
    +        package_app = j.servers.appserver.make_main_app()
    +        for bottle_server in package.bottle_servers:
    +            path = j.sals.fs.join_paths(package.path, bottle_server["file_path"])
    +            if not j.sals.fs.exists(path):
    +                raise j.exceptions.NotFound(f"Cannot find bottle server path {path}")
    +
    +            standalone = bottle_server.get("standalone", False)
    +            if standalone:
    +                bottle_wsgi_server = package.get_bottle_server(path, bottle_server["host"], bottle_server["port"])
    +                self.threebot.rack.add(f"{package.name}_{bottle_server['name']}", bottle_wsgi_server)
    +            else:
    +                bottle_app = package.get_package_bottle_app(path)
    +                package_app.merge(bottle_app)
    +
    +        if package_app.routes:
    +            j.logger.info(f"registering {package.name} package app")
    +            self.threebot.mainapp.mount(f"/{package.name}", package_app)
    +
    +        # register gedis actors
    +        if package.actors_dir:
    +            for actor in package.actors:
    +                self.threebot.gedis._system_actor.register_actor(actor["name"], actor["path"], force_reload=True)
    +
    +        # add chatflows actors
    +        if package.chats_dir:
    +            self.threebot.chatbot.load(package.chats_dir)
    +
    +        # start background services
    +        if package.services_dir:
    +            for service in package.services:
    +                self.threebot.services.add_service(service["name"], service["path"])
    +
    +        j.logger.info(f"starting rack")
    +        # start servers
    +        self.threebot.rack.start()
    +
    +        j.logger.info(f"applying nginx config")
    +        # apply nginx configuration
    +        package.nginx_config.apply()
    +
    +        j.logger.info(f"starting package")
    +        # execute package start method
    +        package.start()
    +
    +        j.logger.info(f"reloading gedis")
    +        self.threebot.gedis_http.client.reload()
    +        j.logger.info(f"reloading nginx")
    +        self.threebot.nginx.reload()
    +
    +    def reload(self, package_name):
    +        if self.threebot.started:
    +            package = self.get(package_name)
    +            if not package:
    +                raise j.exceptions.NotFound(f"{package_name} package not found")
    +            if package.services_dir:
    +                for service in package.services:
    +                    self.threebot.services.stop_service(service["name"])
    +            self.install(package)
    +            self.threebot.nginx.reload()
    +            self.save()
    +        else:
    +            raise j.exceptions.Runtime("Can't reload package. Threebot server is not started")
    +
    +        # Return updated package info
    +        return {package.name: self.packages[package.name]}
    +
    +    def _install_all(self):
    +        """Install and apply all the packages configurations
    +        This method shall not be called directly from the shell,
    +        it must be called only from the code on the running Gedis server
    +        """
    +        all_packages = self.list_all()
    +        for package in all_packages:
    +            if package not in DEFAULT_PACKAGES:
    +                j.logger.info(f"Configuring package {package}")
    +                pkg = self.get(package)
    +                if not pkg:
    +                    j.logger.error(f"can't get package {package}")
    +                else:
    +                    if pkg.path and pkg.is_valid():
    +                        self.install(pkg)
    +                    else:
    +                        j.logger.error(f"package {package} was installed before but {pkg.path} doesn't exist anymore.")
    +
    +    def scan_packages_paths_in_dir(self, path):
    +        """Scans all packages in a path in any level and returns list of package paths
    +
    +        Args:
    +            path (str): root path that has packages on some levels
    +
    +        Returns:
    +            List[str]: list of all packages available under the path
    +        """
    +        filterfun = lambda x: str(x).endswith("package.toml")
    +        pkgtoml_paths = j.sals.fs.walk(path, filter_fun=filterfun)
    +        pkgs_paths = list(map(lambda x: x.replace("/package.toml", ""), pkgtoml_paths))
    +        return pkgs_paths
    +
    +    def scan_packages_in_dir(self, path):
    +        """Gets a dict from packages names to packages paths existing under a path that may have jumpscale packages at any level.
    +
    +        Args:
    +            path (str): root path that has packages on some levels
    +
    +        Returns:
    +            Dict[package_name, package_path]: dict of all packages available under the path
    +        """
    +        pkgname_to_path = {}
    +        for p in self.scan_packages_paths_in_dir(path):
    +            basename = j.sals.fs.basename(p).strip()
    +            if basename:
    +                pkgname_to_path[basename] = p
    +
    +        return pkgname_to_path
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var packages
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var threebot
    +
    +
    +
    + +Expand source code + +
    @property
    +def threebot(self):
    +    if self._threebot is None:
    +        self._threebot = j.servers.threebot.get()
    +    return self._threebot
    +
    +
    +
    +

    Methods

    +
    +
    +def add(self, path: str = None, giturl: str = None, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def add(self, path: str = None, giturl: str = None, **kwargs):
    +    # first check if public repo
    +    # TODO: Check if package already exists
    +    if not any([path, giturl]) or all([path, giturl]):
    +        raise j.exceptions.Value("either path or giturl is required")
    +    pkg_name = ""
    +    if giturl:
    +        url = urlparse(giturl)
    +        url_parts = url.path.lstrip("/").split("/")
    +        if len(url_parts) == 2:
    +            pkg_name = url_parts[1].strip("/")
    +            j.logger.debug(
    +                f"user didn't pass a URL containing branch {giturl}, try to guess (master, main, development) in order"
    +            )
    +            if j.tools.http.get(f"{giturl}/tree/master").status_code == 200:
    +                url_parts.extend(["tree", "master"])
    +            elif j.tools.http.get(f"{giturl}/tree/main").status_code == 200:
    +                url_parts.extend(["tree", "main"])
    +            elif j.tools.http.get(f"{giturl}/tree/development").status_code == 200:
    +                url_parts.extend(["tree", "development"])
    +            else:
    +                raise j.exceptions.Value(f"couldn't guess the branch for {giturl}")
    +        else:
    +            pkg_name = url_parts[-1].strip("/")
    +
    +        if len(url_parts) < 4:
    +            raise j.exceptions.Value(f"invalid git URL {giturl}")
    +
    +        org, repo, _, branch = url_parts[:4]
    +        repo_dir = f"{org}_{repo}_{pkg_name}_{branch}"
    +        repo_path = j.sals.fs.join_paths(DOWNLOADED_PACKAGES_PATH, repo_dir)
    +        repo_url = f"{url.scheme}://{url.hostname}/{org}/{repo}"
    +
    +        # delete repo dir if exists
    +        j.sals.fs.rmtree(repo_path)
    +
    +        j.tools.git.clone_repo(url=repo_url, dest=repo_path, branch_or_tag=branch)
    +        toml_paths = list(
    +            j.sals.fs.walk(repo_path, "*", filter_fun=lambda x: str(x).endswith(f"{pkg_name}/package.toml"))
    +        )
    +        if not toml_paths:
    +            raise j.exceptions.Value(f"couldn't find {pkg_name}/package.toml in {repo_path}")
    +        path_for_package_toml = toml_paths[0]
    +        package_path = j.sals.fs.parent(path_for_package_toml)
    +        path = package_path
    +
    +    admins = kwargs.pop("admins", [])
    +
    +    package = Package(
    +        path=path,
    +        default_domain=self.threebot.domain,
    +        default_email=self.threebot.email,
    +        giturl=giturl,
    +        kwargs=kwargs,
    +        admins=admins,
    +    )
    +
    +    # TODO: adding under the same name if same path and same giturl should be fine, no?
    +    # if package.name in self.packages:
    +    #     raise j.exceptions.Value(f"Package with name {package.name} already exists")
    +
    +    # execute package install method
    +    package.install(**kwargs)
    +
    +    # install package if threebot is started
    +    if self.threebot.started:
    +        self.install(package)
    +        self.threebot.nginx.reload()
    +    self.packages[package.name] = {
    +        "name": package.name,
    +        "path": package.path,
    +        "giturl": package.giturl,
    +        "kwargs": package.kwargs,
    +        "admins": package.admins,
    +        "ui_name": package.ui_name,
    +    }
    +
    +    self.save()
    +
    +    # Return updated package info
    +    return {package.name: self.packages[package.name]}
    +
    +
    +
    +def delete(self, package_name) +
    +
    +
    +
    + +Expand source code + +
    def delete(self, package_name):
    +    if package_name in DEFAULT_PACKAGES:
    +        raise j.exceptions.Value("cannot delete default packages")
    +    package = self.get(package_name)
    +    if not package:
    +        raise j.exceptions.NotFound(f"{package_name} package not found")
    +
    +    # remove bottle servers
    +    rack_servers = list(self.threebot.rack._servers)
    +    for bottle_server in rack_servers:
    +        if bottle_server.startswith(f"{package_name}_"):
    +            self.threebot.rack.remove(bottle_server)
    +
    +    # stop background services
    +    if package.services_dir:
    +        for service in package.services:
    +            self.threebot.services.stop_service(service["name"])
    +
    +    if self.threebot.started:
    +        # unregister gedis actors
    +        gedis_actors = list(self.threebot.gedis._loaded_actors.keys())
    +        for actor in gedis_actors:
    +            if actor.startswith(f"{package_name}_"):
    +                self.threebot.gedis._system_actor.unregister_actor(actor)
    +
    +        # unload chats
    +        try:
    +            if package.chats_dir:
    +                self.threebot.chatbot.unload(package.chats_dir)
    +        except Exception as e:
    +            j.logger.warning(f"Couldn't unload the chats of package {package_name}, this is the exception {str(e)}")
    +
    +        # reload nginx
    +        self.threebot.nginx.reload()
    +
    +    # execute package uninstall method
    +    package.uninstall()
    +
    +    self.packages.pop(package_name)
    +    self.save()
    +
    +
    +
    +def get(self, package_name) +
    +
    +
    +
    + +Expand source code + +
    def get(self, package_name):
    +    if package_name in self.packages:
    +        package_path = self.packages[package_name]["path"]
    +        package_giturl = self.packages[package_name]["giturl"]
    +        package_kwargs = self.packages[package_name].get("kwargs", {})
    +        package_admins = self.packages[package_name].get("admins", [])
    +
    +        return Package(
    +            path=package_path,
    +            default_domain=self.threebot.domain,
    +            default_email=self.threebot.email,
    +            default_acme_server_type=self.threebot.acme_server_type,
    +            default_acme_server_url=self.threebot.acme_server_url,
    +            giturl=package_giturl,
    +            kwargs=package_kwargs,
    +            admins=package_admins,
    +        )
    +
    +
    +
    +def get_packages(self) +
    +
    +
    +
    + +Expand source code + +
    def get_packages(self):
    +    all_packages = []
    +
    +    # Add installed packages including outer packages
    +    for pkg in self.packages:
    +        package = self.get(pkg)
    +
    +        if package and package.is_valid():
    +            if j.sals.fs.exists(package.path):
    +                chatflows = True if package.chats_dir else False
    +                all_packages.append(
    +                    {
    +                        "name": pkg,
    +                        "path": package.path,
    +                        "giturl": package.giturl,
    +                        "system_package": pkg in DEFAULT_PACKAGES.keys(),
    +                        "installed": True,
    +                        "frontend": package.config.get("frontend", False),
    +                        "chatflows": chatflows,
    +                        "ui_name": package.ui_name,
    +                    }
    +                )
    +            else:
    +                j.logger.error(f"path {package.path} for {pkg} doesn't exist anymore")
    +        else:
    +            j.logger.error("pkg {pkg} is in self.packages but it's None")
    +
    +    # Add uninstalled sdk packages under j.packages
    +    for path in set(pkgnamespace.__path__):
    +        for pkg in os.listdir(path):
    +            pkg_path = j.sals.fs.join_paths(path, pkg)
    +            pkgtoml_path = j.sals.fs.join_paths(pkg_path, "package.toml")
    +            ui_name = pkg
    +            excluded = False
    +            with open(pkgtoml_path) as f:
    +                conf = j.data.serializers.toml.loads(f.read())
    +                ui_name = conf.get("ui_name", pkg)
    +                excluded = conf.get("excluded", False)
    +            if pkg not in self.packages and j.sals.fs.exists(pkgtoml_path) and not excluded:
    +                all_packages.append(
    +                    {
    +                        "name": pkg,
    +                        "path": j.sals.fs.dirname(getattr(j.packages, pkg).__file__),
    +                        "giturl": "",
    +                        "system_package": pkg in DEFAULT_PACKAGES.keys(),
    +                        "installed": False,
    +                        "ui_name": ui_name,
    +                    }
    +                )
    +
    +    return all_packages
    +
    +
    +
    +def install(self, package) +
    +
    +

    install and apply package configrations

    +

    Args

    +
    +
    package : [package object]
    +
    get package object using [self.get(package_name)]
    +
    +

    Returns

    +
    +
    [dict]
    +
    [package info]
    +
    +
    + +Expand source code + +
    def install(self, package):
    +    """install and apply package configrations
    +
    +    Args:
    +        package ([package object]): get package object using [self.get(package_name)]
    +
    +    Returns:
    +        [dict]: [package info]
    +    """
    +    sys.path.append(package.path + "/../")  # TODO to be changed
    +    package.preinstall()
    +    for static_dir in package.static_dirs:
    +        path = package.resolve_staticdir_location(static_dir)
    +        if not j.sals.fs.exists(path):
    +            raise j.exceptions.NotFound(f"Cannot find static dir {path}")
    +
    +    # add bottle servers
    +    # we first merge all apps of a package into a single app
    +    # then mount this app on threebot main app
    +    # this will work with multiple non-standalone apps
    +    package_app = j.servers.appserver.make_main_app()
    +    for bottle_server in package.bottle_servers:
    +        path = j.sals.fs.join_paths(package.path, bottle_server["file_path"])
    +        if not j.sals.fs.exists(path):
    +            raise j.exceptions.NotFound(f"Cannot find bottle server path {path}")
    +
    +        standalone = bottle_server.get("standalone", False)
    +        if standalone:
    +            bottle_wsgi_server = package.get_bottle_server(path, bottle_server["host"], bottle_server["port"])
    +            self.threebot.rack.add(f"{package.name}_{bottle_server['name']}", bottle_wsgi_server)
    +        else:
    +            bottle_app = package.get_package_bottle_app(path)
    +            package_app.merge(bottle_app)
    +
    +    if package_app.routes:
    +        j.logger.info(f"registering {package.name} package app")
    +        self.threebot.mainapp.mount(f"/{package.name}", package_app)
    +
    +    # register gedis actors
    +    if package.actors_dir:
    +        for actor in package.actors:
    +            self.threebot.gedis._system_actor.register_actor(actor["name"], actor["path"], force_reload=True)
    +
    +    # add chatflows actors
    +    if package.chats_dir:
    +        self.threebot.chatbot.load(package.chats_dir)
    +
    +    # start background services
    +    if package.services_dir:
    +        for service in package.services:
    +            self.threebot.services.add_service(service["name"], service["path"])
    +
    +    j.logger.info(f"starting rack")
    +    # start servers
    +    self.threebot.rack.start()
    +
    +    j.logger.info(f"applying nginx config")
    +    # apply nginx configuration
    +    package.nginx_config.apply()
    +
    +    j.logger.info(f"starting package")
    +    # execute package start method
    +    package.start()
    +
    +    j.logger.info(f"reloading gedis")
    +    self.threebot.gedis_http.client.reload()
    +    j.logger.info(f"reloading nginx")
    +    self.threebot.nginx.reload()
    +
    +
    +
    +def list_all(self) +
    +
    +
    +
    + +Expand source code + +
    def list_all(self):
    +    return list(self.packages.keys())
    +
    +
    +
    +def reload(self, package_name) +
    +
    +
    +
    + +Expand source code + +
    def reload(self, package_name):
    +    if self.threebot.started:
    +        package = self.get(package_name)
    +        if not package:
    +            raise j.exceptions.NotFound(f"{package_name} package not found")
    +        if package.services_dir:
    +            for service in package.services:
    +                self.threebot.services.stop_service(service["name"])
    +        self.install(package)
    +        self.threebot.nginx.reload()
    +        self.save()
    +    else:
    +        raise j.exceptions.Runtime("Can't reload package. Threebot server is not started")
    +
    +    # Return updated package info
    +    return {package.name: self.packages[package.name]}
    +
    +
    +
    +def scan_packages_in_dir(self, path) +
    +
    +

    Gets a dict from packages names to packages paths existing under a path that may have jumpscale packages at any level.

    +

    Args

    +
    +
    path : str
    +
    root path that has packages on some levels
    +
    +

    Returns

    +
    +
    Dict[package_name, package_path]
    +
    dict of all packages available under the path
    +
    +
    + +Expand source code + +
    def scan_packages_in_dir(self, path):
    +    """Gets a dict from packages names to packages paths existing under a path that may have jumpscale packages at any level.
    +
    +    Args:
    +        path (str): root path that has packages on some levels
    +
    +    Returns:
    +        Dict[package_name, package_path]: dict of all packages available under the path
    +    """
    +    pkgname_to_path = {}
    +    for p in self.scan_packages_paths_in_dir(path):
    +        basename = j.sals.fs.basename(p).strip()
    +        if basename:
    +            pkgname_to_path[basename] = p
    +
    +    return pkgname_to_path
    +
    +
    +
    +def scan_packages_paths_in_dir(self, path) +
    +
    +

    Scans all packages in a path in any level and returns list of package paths

    +

    Args

    +
    +
    path : str
    +
    root path that has packages on some levels
    +
    +

    Returns

    +
    +
    List[str]
    +
    list of all packages available under the path
    +
    +
    + +Expand source code + +
    def scan_packages_paths_in_dir(self, path):
    +    """Scans all packages in a path in any level and returns list of package paths
    +
    +    Args:
    +        path (str): root path that has packages on some levels
    +
    +    Returns:
    +        List[str]: list of all packages available under the path
    +    """
    +    filterfun = lambda x: str(x).endswith("package.toml")
    +    pkgtoml_paths = j.sals.fs.walk(path, filter_fun=filterfun)
    +    pkgs_paths = list(map(lambda x: x.replace("/package.toml", ""), pkgtoml_paths))
    +    return pkgs_paths
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class ThreebotServer +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class ThreebotServer(Base):
    +    _package_manager = fields.Factory(PackageManager)
    +    domain = fields.String()
    +    email = fields.String()
    +    acme_server_type = fields.Enum(AcmeServer)
    +    acme_server_url = fields.URL()
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._rack = None
    +        self._gedis = None
    +        self._db = None
    +        self._gedis_http = None
    +        self._services = None
    +        self._packages = None
    +        self._started = False
    +        self._nginx = None
    +        self._redis = None
    +        self.rack.add(GEDIS, self.gedis)
    +        self.rack.add(GEDIS_HTTP, self.gedis_http.gevent_server)
    +        self.rack.add(SERVICE_MANAGER, self.services)
    +
    +    def is_running(self):
    +        nginx_running = self.nginx.is_running()
    +        redis_running = self.redis.cmd.is_running() or j.sals.nettools.wait_connection_test(
    +            "127.0.0.1", 6379, timeout=1
    +        )
    +        gedis_running = j.sals.nettools.wait_connection_test("127.0.0.1", 16000, timeout=1)
    +        return nginx_running and redis_running and gedis_running
    +
    +    @property
    +    def started(self):
    +        return self._started
    +
    +    @property
    +    def nginx(self):
    +        if self._nginx is None:
    +            self._nginx = j.tools.nginx.get("default")
    +        return self._nginx
    +
    +    @property
    +    def redis(self):
    +        if self._redis is None:
    +            self._redis = j.tools.redis.get("default")
    +        return self._redis
    +
    +    @property
    +    def db(self):
    +        if self._db is None:
    +            self._db = j.core.db
    +        return self._db
    +
    +    @property
    +    def rack(self):
    +        if self._rack is None:
    +            self._rack = j.servers.rack
    +        return self._rack
    +
    +    @property
    +    def gedis(self):
    +        if self._gedis is None:
    +            self._gedis = j.servers.gedis.get("threebot")
    +        return self._gedis
    +
    +    @property
    +    def gedis_http(self):
    +        if self._gedis_http is None:
    +            self._gedis_http = j.servers.gedis_http.get("threebot")
    +        return self._gedis_http
    +
    +    @property
    +    def services(self):
    +        if self._services is None:
    +            self._services = j.tools.servicemanager.get("threebot")
    +        return self._services
    +
    +    @property
    +    def chatbot(self):
    +        return self.gedis._loaded_actors.get("chatflows_chatbot")
    +
    +    @property
    +    def packages(self):
    +        if self._packages is None:
    +            self._packages = self._package_manager.get(self.instance_name)
    +        return self._packages
    +
    +    def check_dependencies(self):
    +        install_msg = "Visit https://github.com/threefoldtech/js-sdk/blob/development/docs/wiki/quick_start.md for installation guide"
    +
    +        if not self.nginx.installed:
    +            raise j.exceptions.NotFound(f"nginx is not installed.\n{install_msg}")
    +
    +        ret = shutil.which("certbot")
    +        if not ret:
    +            raise j.exceptions.NotFound(f"certbot is not installed.\n{install_msg}")
    +
    +        rc, out, err = j.sals.process.execute("certbot plugins")
    +        if "* nginx" not in out:
    +            raise j.exceptions.NotFound(f"python-certbot-nginx is not installed.\n{install_msg}")
    +
    +        if not self.redis.installed:
    +            raise j.exceptions.NotFound(f"redis is not installed.\n{install_msg}")
    +
    +        ret = shutil.which("tmux")
    +        if not ret:
    +            raise j.exceptions.NotFound(f"tmux is not installed.\n{install_msg}")
    +
    +        ret = shutil.which("git")
    +        if not ret:
    +            raise j.exceptions.NotFound(f"git is not installed.\n{install_msg}")
    +
    +    def start(self, wait: bool = False, cert: bool = True):
    +        # start default servers in the rack
    +        # handle signals
    +        for signal_type in (signal.SIGTERM, signal.SIGINT, signal.SIGKILL):
    +            gevent.signal_handler(signal_type, self.stop)
    +
    +        # mark app as started
    +        if self.is_running():
    +            return
    +
    +        self.check_dependencies()
    +
    +        self.redis.start()
    +        self.nginx.start()
    +        j.sals.nginx.get(self.nginx.server_name).cert = cert
    +        self.mainapp = j.servers.appserver.make_main_app()
    +
    +        self.rack.start()
    +        j.logger.register(f"threebot_{self.instance_name}")
    +        # add default packages
    +        for package_name in DEFAULT_PACKAGES:
    +            j.logger.info(f"Configuring package {package_name}")
    +            try:
    +                package = self.packages.get(package_name)
    +                self.packages.install(package)
    +            except Exception as e:
    +                self.stop()
    +                raise j.core.exceptions.Runtime(
    +                    f"Error happened during getting or installing {package_name} package, the detailed error is {str(e)}"
    +                ) from e
    +
    +        # install all package
    +
    +        j.logger.info("Adding packages")
    +        self.packages._install_all()
    +        j.logger.info("jsappserver")
    +        self.jsappserver = WSGIServer(("localhost", 31000), apply_main_middlewares(self.mainapp))
    +        j.logger.info("rack add")
    +        self.rack.add(f"appserver", self.jsappserver)
    +
    +        j.logger.info("Reloading nginx")
    +        self.nginx.reload()
    +
    +        # mark server as started
    +        self._started = True
    +        j.logger.info(f"routes: {self.mainapp.routes}")
    +        j.logger.info(f"Threebot is running at http://localhost:{PORTS.HTTP} and https://localhost:{PORTS.HTTPS}")
    +        self.rack.start(wait=wait)  # to keep the server running
    +
    +    def stop(self):
    +        server_packages = self.packages.list_all()
    +        for package_name in server_packages:
    +            package = self.packages.get(package_name)
    +            package.stop()
    +        self.nginx.stop()
    +        # mark app as stopped, do this before stopping redis
    +        j.logger.unregister()
    +        self.rack.stop()
    +        self.redis.stop()
    +        self._started = False
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var acme_server_type
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var acme_server_url
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var chatbot
    +
    +
    +
    + +Expand source code + +
    @property
    +def chatbot(self):
    +    return self.gedis._loaded_actors.get("chatflows_chatbot")
    +
    +
    +
    var db
    +
    +
    +
    + +Expand source code + +
    @property
    +def db(self):
    +    if self._db is None:
    +        self._db = j.core.db
    +    return self._db
    +
    +
    +
    var domain
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var email
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var gedis
    +
    +
    +
    + +Expand source code + +
    @property
    +def gedis(self):
    +    if self._gedis is None:
    +        self._gedis = j.servers.gedis.get("threebot")
    +    return self._gedis
    +
    +
    +
    var gedis_http
    +
    +
    +
    + +Expand source code + +
    @property
    +def gedis_http(self):
    +    if self._gedis_http is None:
    +        self._gedis_http = j.servers.gedis_http.get("threebot")
    +    return self._gedis_http
    +
    +
    +
    var nginx
    +
    +
    +
    + +Expand source code + +
    @property
    +def nginx(self):
    +    if self._nginx is None:
    +        self._nginx = j.tools.nginx.get("default")
    +    return self._nginx
    +
    +
    +
    var packages
    +
    +
    +
    + +Expand source code + +
    @property
    +def packages(self):
    +    if self._packages is None:
    +        self._packages = self._package_manager.get(self.instance_name)
    +    return self._packages
    +
    +
    +
    var rack
    +
    +
    +
    + +Expand source code + +
    @property
    +def rack(self):
    +    if self._rack is None:
    +        self._rack = j.servers.rack
    +    return self._rack
    +
    +
    +
    var redis
    +
    +
    +
    + +Expand source code + +
    @property
    +def redis(self):
    +    if self._redis is None:
    +        self._redis = j.tools.redis.get("default")
    +    return self._redis
    +
    +
    +
    var services
    +
    +
    +
    + +Expand source code + +
    @property
    +def services(self):
    +    if self._services is None:
    +        self._services = j.tools.servicemanager.get("threebot")
    +    return self._services
    +
    +
    +
    var started
    +
    +
    +
    + +Expand source code + +
    @property
    +def started(self):
    +    return self._started
    +
    +
    +
    +

    Methods

    +
    +
    +def check_dependencies(self) +
    +
    +
    +
    + +Expand source code + +
    def check_dependencies(self):
    +    install_msg = "Visit https://github.com/threefoldtech/js-sdk/blob/development/docs/wiki/quick_start.md for installation guide"
    +
    +    if not self.nginx.installed:
    +        raise j.exceptions.NotFound(f"nginx is not installed.\n{install_msg}")
    +
    +    ret = shutil.which("certbot")
    +    if not ret:
    +        raise j.exceptions.NotFound(f"certbot is not installed.\n{install_msg}")
    +
    +    rc, out, err = j.sals.process.execute("certbot plugins")
    +    if "* nginx" not in out:
    +        raise j.exceptions.NotFound(f"python-certbot-nginx is not installed.\n{install_msg}")
    +
    +    if not self.redis.installed:
    +        raise j.exceptions.NotFound(f"redis is not installed.\n{install_msg}")
    +
    +    ret = shutil.which("tmux")
    +    if not ret:
    +        raise j.exceptions.NotFound(f"tmux is not installed.\n{install_msg}")
    +
    +    ret = shutil.which("git")
    +    if not ret:
    +        raise j.exceptions.NotFound(f"git is not installed.\n{install_msg}")
    +
    +
    +
    +def is_running(self) +
    +
    +
    +
    + +Expand source code + +
    def is_running(self):
    +    nginx_running = self.nginx.is_running()
    +    redis_running = self.redis.cmd.is_running() or j.sals.nettools.wait_connection_test(
    +        "127.0.0.1", 6379, timeout=1
    +    )
    +    gedis_running = j.sals.nettools.wait_connection_test("127.0.0.1", 16000, timeout=1)
    +    return nginx_running and redis_running and gedis_running
    +
    +
    +
    +def start(self, wait: bool = False, cert: bool = True) +
    +
    +
    +
    + +Expand source code + +
    def start(self, wait: bool = False, cert: bool = True):
    +    # start default servers in the rack
    +    # handle signals
    +    for signal_type in (signal.SIGTERM, signal.SIGINT, signal.SIGKILL):
    +        gevent.signal_handler(signal_type, self.stop)
    +
    +    # mark app as started
    +    if self.is_running():
    +        return
    +
    +    self.check_dependencies()
    +
    +    self.redis.start()
    +    self.nginx.start()
    +    j.sals.nginx.get(self.nginx.server_name).cert = cert
    +    self.mainapp = j.servers.appserver.make_main_app()
    +
    +    self.rack.start()
    +    j.logger.register(f"threebot_{self.instance_name}")
    +    # add default packages
    +    for package_name in DEFAULT_PACKAGES:
    +        j.logger.info(f"Configuring package {package_name}")
    +        try:
    +            package = self.packages.get(package_name)
    +            self.packages.install(package)
    +        except Exception as e:
    +            self.stop()
    +            raise j.core.exceptions.Runtime(
    +                f"Error happened during getting or installing {package_name} package, the detailed error is {str(e)}"
    +            ) from e
    +
    +    # install all package
    +
    +    j.logger.info("Adding packages")
    +    self.packages._install_all()
    +    j.logger.info("jsappserver")
    +    self.jsappserver = WSGIServer(("localhost", 31000), apply_main_middlewares(self.mainapp))
    +    j.logger.info("rack add")
    +    self.rack.add(f"appserver", self.jsappserver)
    +
    +    j.logger.info("Reloading nginx")
    +    self.nginx.reload()
    +
    +    # mark server as started
    +    self._started = True
    +    j.logger.info(f"routes: {self.mainapp.routes}")
    +    j.logger.info(f"Threebot is running at http://localhost:{PORTS.HTTP} and https://localhost:{PORTS.HTTPS}")
    +    self.rack.start(wait=wait)  # to keep the server running
    +
    +
    +
    +def stop(self) +
    +
    +
    +
    + +Expand source code + +
    def stop(self):
    +    server_packages = self.packages.list_all()
    +    for package_name in server_packages:
    +        package = self.packages.get(package_name)
    +        package.stop()
    +    self.nginx.stop()
    +    # mark app as stopped, do this before stopping redis
    +    j.logger.unregister()
    +    self.rack.stop()
    +    self.redis.stop()
    +    self._started = False
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/shell/config.html b/docs/api/jumpscale/shell/config.html index e64f061a1..c2e1f25d1 100644 --- a/docs/api/jumpscale/shell/config.html +++ b/docs/api/jumpscale/shell/config.html @@ -639,4 +639,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/shell/index.html b/docs/api/jumpscale/shell/index.html index e40142a20..9b0ad2ed5 100644 --- a/docs/api/jumpscale/shell/index.html +++ b/docs/api/jumpscale/shell/index.html @@ -77,4 +77,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/alerthandler/alerthandler.html b/docs/api/jumpscale/tools/alerthandler/alerthandler.html index bb937eb3d..582015887 100644 --- a/docs/api/jumpscale/tools/alerthandler/alerthandler.html +++ b/docs/api/jumpscale/tools/alerthandler/alerthandler.html @@ -1039,4 +1039,4 @@

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/alerthandler/index.html b/docs/api/jumpscale/tools/alerthandler/index.html index 46e79c620..5a252b9e9 100644 --- a/docs/api/jumpscale/tools/alerthandler/index.html +++ b/docs/api/jumpscale/tools/alerthandler/index.html @@ -92,4 +92,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/codeloader/index.html b/docs/api/jumpscale/tools/codeloader/index.html index 89f6ca247..86db6099c 100644 --- a/docs/api/jumpscale/tools/codeloader/index.html +++ b/docs/api/jumpscale/tools/codeloader/index.html @@ -173,4 +173,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/console/index.html b/docs/api/jumpscale/tools/console/index.html index 1d72f308e..fa77b8b20 100644 --- a/docs/api/jumpscale/tools/console/index.html +++ b/docs/api/jumpscale/tools/console/index.html @@ -25,8 +25,8 @@

    Module jumpscale.tools.console

    Console module helps with coloring in the console and asking for input from the user.

    -
    JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")
    -JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")
    +
    JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")                                                                     
    +JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")                                                              
     
    @@ -34,8 +34,8 @@

    Module jumpscale.tools.console

    """Console module helps with coloring in the console and asking for input from the user.
     ```
    -JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")
    -JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")
    +JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{WHITE}OK")                                                                     
    +JS-NG> j.tools.console.printcolors("{RED}Hello{BGRED}What{RESET}{WHITE}OK")                                                              
     ```
     
     """
    @@ -77,11 +77,11 @@ 

    Module jumpscale.tools.console

    def ask_password(prompt="Password : ", forbiddens=[]): """Prompt the user for a password without echoing - + Keyword Arguments: prompt {str} -- the question message (default: {"Password : "}) forbiddens {list} -- the list of bad passwords (default: {[]}) - + Returns: str -- the appropriate input password """ @@ -94,12 +94,12 @@

    Module jumpscale.tools.console

    def ask_yes_no(prompt="[y/n] :", default="y", valid=["y", "n"]): """Display a yes/no question and loop until a valid answer is entered - + Keyword Arguments: prompt {str} -- the question message (default: {'[y/n] :'}) default {str} -- the default answer if there is no answer (default: {"y"}) valid {list} -- the list of appropriate answers (default: {["y", "n"]}) - + Returns: str -- the answer """ @@ -122,14 +122,14 @@

    Module jumpscale.tools.console

    def ask_int_in_range(mini, maxi, prompt="Type int :"): """Get an integer response between two integer on asked question - + Arguments: mini {int} -- the minimum value for the number maxi {int} -- the maximum value for the number - + Keyword Arguments: prompt {str} -- the question message (default: {"Type int :"}) - + Returns: int -- the input number on the range provided """ @@ -152,14 +152,14 @@

    Module jumpscale.tools.console

    def ask_float_in_range(mini, maxi, prompt="Type float :"): """Get an float response between two float on asked question - + Arguments: mini {float} -- the minimum value for the number maxi {float} -- the maximum value for the number - + Keyword Arguments: prompt {str} -- the question message (default: {"Type float :"}) - + Returns: float -- the input number on the range provided """ @@ -184,11 +184,11 @@

    Module jumpscale.tools.console

    def ask_choice(prompt="Type choice number : ", choices_list=[]): """Get an option from provided list - + Keyword Arguments: prompt {str} -- the question message (default: {"Type choice number : "}) choices_list {list} -- the available options (default: {[]}) - + Returns: str -- the chosen option """ @@ -202,13 +202,13 @@

    Module jumpscale.tools.console

    def ask_multi_choices(prompt="Add to choices : ", choices_list=[], to_save="s", to_quit="q"): """Collect multi choices from list - + Keyword Arguments: prompt {str} -- the question method (default: {"Add to choices : "}) choices_list {list} -- the available options (default: {[]}) to_save {str} -- escape and save choices (default: {"s"}) to_quit {str} -- escape without saving (default: {"q"}) - + Returns: list -- list of the selected choices """ @@ -231,11 +231,11 @@

    Module jumpscale.tools.console

    def ask_multi_lines(prompt="Type :", escape_string="."): """Get input from user provided multilines - + Keyword Arguments: prompt {str} -- the question message (default: {"Type :"}) escape_string {str} -- escape character (default: {"."}) - + Returns: str -- the text seperated by lines """ @@ -249,10 +249,10 @@

    Module jumpscale.tools.console

    def ask_string(prompt="Type :"): """Just input function - + Keyword Arguments: prompt {str} -- the question message (default: {"Type :"}) - + Returns: str -- the string input """ @@ -290,11 +290,11 @@

    Returns

    def ask_choice(prompt="Type choice number : ", choices_list=[]):
         """Get an option from provided list
    -
    +    
         Keyword Arguments:
             prompt {str} -- the question message (default: {"Type choice number : "})
             choices_list {list} -- the available options (default: {[]})
    -
    +    
         Returns:
             str -- the chosen option
         """
    @@ -340,14 +340,14 @@ 

    Returns

    def ask_float_in_range(mini, maxi, prompt="Type float :"):
         """Get an float response between two float on asked question
    -
    +    
         Arguments:
             mini {float} -- the minimum value for the number
             maxi {float} -- the maximum value for the number
    -
    +    
         Keyword Arguments:
             prompt {str} -- the question message (default: {"Type float :"})
    -
    +    
         Returns:
             float -- the input number on the range provided
         """
    @@ -395,14 +395,14 @@ 

    Returns

    def ask_int_in_range(mini, maxi, prompt="Type int :"):
         """Get an integer response between two integer on asked question
    -
    +    
         Arguments:
             mini {int} -- the minimum value for the number
             maxi {int} -- the maximum value for the number
    -
    +    
         Keyword Arguments:
             prompt {str} -- the question message (default: {"Type int :"})
    -
    +    
         Returns:
             int -- the input number on the range provided
         """
    @@ -434,13 +434,13 @@ 

    Returns

    def ask_multi_choices(prompt="Add to choices : ", choices_list=[], to_save="s", to_quit="q"):
         """Collect multi choices from list
    -
    +    
         Keyword Arguments:
             prompt {str} -- the question method (default: {"Add to choices : "})
             choices_list {list} -- the available options (default: {[]})
             to_save {str} -- escape and save choices (default: {"s"})
             to_quit {str} -- escape without saving (default: {"q"})
    -
    +    
         Returns:
             list -- list of the selected choices
         """
    @@ -477,11 +477,11 @@ 

    Returns

    def ask_multi_lines(prompt="Type :", escape_string="."):
         """Get input from user provided multilines
    -
    +    
         Keyword Arguments:
             prompt {str} -- the question message (default: {"Type :"})
             escape_string {str} -- escape character (default: {"."})
    -
    +    
         Returns:
             str -- the text seperated by lines
         """
    @@ -509,11 +509,11 @@ 

    Returns

    def ask_password(prompt="Password : ", forbiddens=[]):
         """Prompt the user for a password without echoing
    -
    +    
         Keyword Arguments:
             prompt {str} -- the question message (default: {"Password : "})
             forbiddens {list} -- the list of bad passwords (default: {[]})
    -
    +    
         Returns:
             str -- the appropriate input password
         """
    @@ -539,10 +539,10 @@ 

    Returns

    def ask_string(prompt="Type :"):
         """Just input function
    -
    +    
         Keyword Arguments:
             prompt {str} -- the question message (default: {"Type :"})
    -
    +    
         Returns:
             str -- the string input
         """
    @@ -568,12 +568,12 @@ 

    Returns

    def ask_yes_no(prompt="[y/n] :", default="y", valid=["y", "n"]):
         """Display a yes/no question and loop until a valid answer is entered
    -
    +    
         Keyword Arguments:
             prompt {str} -- the question message (default: {'[y/n] :'})
             default {str} -- the default answer if there is no answer (default: {"y"})
             valid {list} -- the list of appropriate answers (default: {["y", "n"]})
    -
    +    
         Returns:
             str -- the answer
         """
    @@ -683,4 +683,4 @@ 

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/depsresolver/depsresolver.html b/docs/api/jumpscale/tools/depsresolver/depsresolver.html index 84492d7e6..052a42a2e 100644 --- a/docs/api/jumpscale/tools/depsresolver/depsresolver.html +++ b/docs/api/jumpscale/tools/depsresolver/depsresolver.html @@ -439,4 +439,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/depsresolver/index.html b/docs/api/jumpscale/tools/depsresolver/index.html index 29d10d4f9..51f2b6e29 100644 --- a/docs/api/jumpscale/tools/depsresolver/index.html +++ b/docs/api/jumpscale/tools/depsresolver/index.html @@ -94,4 +94,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/dnstool/index.html b/docs/api/jumpscale/tools/dnstool/index.html new file mode 100644 index 000000000..f56e63d6e --- /dev/null +++ b/docs/api/jumpscale/tools/dnstool/index.html @@ -0,0 +1,273 @@ + + + + + + +jumpscale.tools.dnstool API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.dnstool

    +
    +
    +

    Helper to get nameservers information and resolving domains.

    +
    + +Expand source code + +
    """
    +Helper to get nameservers information and resolving domains.
    +
    +"""
    +
    +import dns
    +import dns.message
    +import dns.rdataclass
    +import dns.rdatatype
    +import dns.query
    +import dns.resolver
    +
    +
    +class DNSClient:
    +    def __init__(self, nameservers=None, port=53):
    +        self.nameservers = nameservers or ["8.8.8.8", "8.8.4.4"]
    +        if "localhost" in self.nameservers:
    +            nameservers.pop(nameservers.index("localhost"))
    +            nameservers.append("127.0.0.1")
    +        self.resolver = dns.resolver.Resolver(configure=False)
    +        self.resolver.nameservers = self.nameservers
    +        self.resolver.port = port
    +
    +    def get_nameservers(self, domain="threefoldtoken.org"):
    +        answer = self.resolver.query(domain, "NS")
    +
    +        res = []
    +        for rr in answer:
    +            res.append(rr.target.to_text())
    +        return res
    +
    +    def get_namerecords(self, url="www.threefoldtoken.org"):
    +        """
    +        return ip addr for a full name
    +        """
    +        answer = self.resolver.query(url, "A")
    +
    +        res = []
    +        for rr in answer:
    +            res.append(rr.address)
    +        return res
    +
    +    def is_free(self, domain, domain_type="A"):
    +        try:
    +            self.query(domain, domain_type)
    +        except:
    +            return True
    +        return False
    +
    +    def query(self, *args, **kwargs):
    +        return self.resolver.query(*args, **kwargs)
    +
    +
    +def export_module_as():
    +    return DNSClient()
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    return DNSClient()
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class DNSClient +(nameservers=None, port=53) +
    +
    +
    +
    + +Expand source code + +
    class DNSClient:
    +    def __init__(self, nameservers=None, port=53):
    +        self.nameservers = nameservers or ["8.8.8.8", "8.8.4.4"]
    +        if "localhost" in self.nameservers:
    +            nameservers.pop(nameservers.index("localhost"))
    +            nameservers.append("127.0.0.1")
    +        self.resolver = dns.resolver.Resolver(configure=False)
    +        self.resolver.nameservers = self.nameservers
    +        self.resolver.port = port
    +
    +    def get_nameservers(self, domain="threefoldtoken.org"):
    +        answer = self.resolver.query(domain, "NS")
    +
    +        res = []
    +        for rr in answer:
    +            res.append(rr.target.to_text())
    +        return res
    +
    +    def get_namerecords(self, url="www.threefoldtoken.org"):
    +        """
    +        return ip addr for a full name
    +        """
    +        answer = self.resolver.query(url, "A")
    +
    +        res = []
    +        for rr in answer:
    +            res.append(rr.address)
    +        return res
    +
    +    def is_free(self, domain, domain_type="A"):
    +        try:
    +            self.query(domain, domain_type)
    +        except:
    +            return True
    +        return False
    +
    +    def query(self, *args, **kwargs):
    +        return self.resolver.query(*args, **kwargs)
    +
    +

    Methods

    +
    +
    +def get_namerecords(self, url='www.threefoldtoken.org') +
    +
    +

    return ip addr for a full name

    +
    + +Expand source code + +
    def get_namerecords(self, url="www.threefoldtoken.org"):
    +    """
    +    return ip addr for a full name
    +    """
    +    answer = self.resolver.query(url, "A")
    +
    +    res = []
    +    for rr in answer:
    +        res.append(rr.address)
    +    return res
    +
    +
    +
    +def get_nameservers(self, domain='threefoldtoken.org') +
    +
    +
    +
    + +Expand source code + +
    def get_nameservers(self, domain="threefoldtoken.org"):
    +    answer = self.resolver.query(domain, "NS")
    +
    +    res = []
    +    for rr in answer:
    +        res.append(rr.target.to_text())
    +    return res
    +
    +
    +
    +def is_free(self, domain, domain_type='A') +
    +
    +
    +
    + +Expand source code + +
    def is_free(self, domain, domain_type="A"):
    +    try:
    +        self.query(domain, domain_type)
    +    except:
    +        return True
    +    return False
    +
    +
    +
    +def query(self, *args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def query(self, *args, **kwargs):
    +    return self.resolver.query(*args, **kwargs)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/errorhandler/errorhandler.html b/docs/api/jumpscale/tools/errorhandler/errorhandler.html index 0fcc1d12d..c60dd11ff 100644 --- a/docs/api/jumpscale/tools/errorhandler/errorhandler.html +++ b/docs/api/jumpscale/tools/errorhandler/errorhandler.html @@ -513,4 +513,4 @@

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/errorhandler/index.html b/docs/api/jumpscale/tools/errorhandler/index.html index 26b974391..5fb32363e 100644 --- a/docs/api/jumpscale/tools/errorhandler/index.html +++ b/docs/api/jumpscale/tools/errorhandler/index.html @@ -94,4 +94,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/export/index.html b/docs/api/jumpscale/tools/export/index.html new file mode 100644 index 000000000..31829ad5d --- /dev/null +++ b/docs/api/jumpscale/tools/export/index.html @@ -0,0 +1,131 @@ + + + + + + +jumpscale.tools.export API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.export

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +import tempfile
    +
    +
    +def export_threebot_state(output=None):
    +    home = j.core.dirs.HOMEDIR
    +    if home is None:
    +        j.tools.console.printcolors("Error: {RED}Home dir is not found{RESET}")
    +        return
    +    config_path = j.sals.fs.join_paths(home, ".config", "jumpscale")
    +    output_tmp_file = tempfile.mkstemp()[1]
    +    alerts_tmp_file = tempfile.mkstemp()[1]
    +    alerts_path = j.sals.fs.join_paths("jumpscale", "logs", "alerts")
    +    target_file = output_tmp_file
    +    try:
    +        j.sals.process.execute(["sh", "-c", f"redis-cli HGETALL alerts > {alerts_tmp_file}"])
    +        tf = j.data.tarfile.tarfile.open(target_file, "w")
    +        tf.add(config_path, arcname="jumpscale")
    +        tf.add(alerts_tmp_file, arcname=alerts_path)
    +        tf.close()
    +        if output is not None:
    +            j.sals.fs.rename(target_file, output)
    +            target_file = output
    +        j.tools.console.printcolors("Success: {GREEN}Export file created successfully at " + target_file + " {RESET}")
    +    finally:
    +        if output_tmp_file != target_file:
    +            j.sals.fs.rmtree(output_tmp_file)
    +        j.sals.fs.rmtree(alerts_tmp_file)
    +    return target_file
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_threebot_state(output=None) +
    +
    +
    +
    + +Expand source code + +
    def export_threebot_state(output=None):
    +    home = j.core.dirs.HOMEDIR
    +    if home is None:
    +        j.tools.console.printcolors("Error: {RED}Home dir is not found{RESET}")
    +        return
    +    config_path = j.sals.fs.join_paths(home, ".config", "jumpscale")
    +    output_tmp_file = tempfile.mkstemp()[1]
    +    alerts_tmp_file = tempfile.mkstemp()[1]
    +    alerts_path = j.sals.fs.join_paths("jumpscale", "logs", "alerts")
    +    target_file = output_tmp_file
    +    try:
    +        j.sals.process.execute(["sh", "-c", f"redis-cli HGETALL alerts > {alerts_tmp_file}"])
    +        tf = j.data.tarfile.tarfile.open(target_file, "w")
    +        tf.add(config_path, arcname="jumpscale")
    +        tf.add(alerts_tmp_file, arcname=alerts_path)
    +        tf.close()
    +        if output is not None:
    +            j.sals.fs.rename(target_file, output)
    +            target_file = output
    +        j.tools.console.printcolors("Success: {GREEN}Export file created successfully at " + target_file + " {RESET}")
    +    finally:
    +        if output_tmp_file != target_file:
    +            j.sals.fs.rmtree(output_tmp_file)
    +        j.sals.fs.rmtree(alerts_tmp_file)
    +    return target_file
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/git/index.html b/docs/api/jumpscale/tools/git/index.html index 2cafb61ac..a41143368 100644 --- a/docs/api/jumpscale/tools/git/index.html +++ b/docs/api/jumpscale/tools/git/index.html @@ -679,4 +679,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/highlighter/index.html b/docs/api/jumpscale/tools/highlighter/index.html index 71b242f78..42a994e49 100644 --- a/docs/api/jumpscale/tools/highlighter/index.html +++ b/docs/api/jumpscale/tools/highlighter/index.html @@ -77,7 +77,7 @@

    Module jumpscale.tools.highlighter

    def _init(self,**kwargs): self.lexers = Lexers() self.formatters = Formatters() - + def print_python(self,text,formatter="terminal"): C=Tools.text_replace(text) @@ -106,7 +106,7 @@

    Module jumpscale.tools.highlighter

    ports = [ 8001, 8001, 8002 ] connection_max = 5000 enabled = true - + """ print_toml(C) print_highlighted(C) @@ -131,7 +131,7 @@

    Module jumpscale.tools.highlighter

    def _init(self,**kwargs): self.lexers = Lexers() self.formatters = Formatters() - + def print_python(self,text,formatter="terminal"): C=Tools.text_replace(text) @@ -160,7 +160,7 @@

    Module jumpscale.tools.highlighter

    ports = [ 8001, 8001, 8002 ] connection_max = 5000 enabled = true - + """ print_toml(C) print_highlighted(C)

    @@ -229,7 +229,7 @@

    Functions

    def _init(self,**kwargs): self.lexers = Lexers() self.formatters = Formatters() - + def print_python(self,text,formatter="terminal"): C=Tools.text_replace(text) @@ -258,7 +258,7 @@

    Functions

    ports = [ 8001, 8001, 8002 ] connection_max = 5000 enabled = true - + """ print_toml(C) print_highlighted(C)
    @@ -295,4 +295,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/http/index.html b/docs/api/jumpscale/tools/http/index.html index f92a9b4f2..0433de4c6 100644 --- a/docs/api/jumpscale/tools/http/index.html +++ b/docs/api/jumpscale/tools/http/index.html @@ -65,4 +65,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/index.html b/docs/api/jumpscale/tools/index.html index 43b7cb62c..852ab6326 100644 --- a/docs/api/jumpscale/tools/index.html +++ b/docs/api/jumpscale/tools/index.html @@ -44,10 +44,18 @@

    Sub-modules

    +
    jumpscale.tools.dnstool
    +
    +

    Helper to get nameservers information and resolving domains.

    +
    jumpscale.tools.errorhandler
    +
    jumpscale.tools.export
    +
    +
    +
    jumpscale.tools.git

    Git module helps with everything around git management like pulling, cloning .. etc …

    @@ -68,10 +76,38 @@

    Sub-modules

    +
    jumpscale.tools.nginx
    +
    +

    Nginx tool …

    +
    +
    jumpscale.tools.notificationsqueue
    +
    +
    +
    +
    jumpscale.tools.poolexecutor
    +
    +
    +
    +
    jumpscale.tools.qrcode
    +
    +
    +
    +
    jumpscale.tools.redis
    +
    +
    +
    +
    jumpscale.tools.restic
    +
    +
    +
    jumpscale.tools.schemac

    schemac …

    +
    jumpscale.tools.servicemanager
    +
    +
    +
    jumpscale.tools.startupcmd
    @@ -83,10 +119,18 @@

    Sub-modules

    ``` JS-NG> xmonader = …

    +
    jumpscale.tools.tfgateway
    +
    +
    +
    jumpscale.tools.timer

    Helps with timing functions and see how long they took …

    +
    jumpscale.tools.wireguard
    +
    +
    +

  • @@ -113,16 +157,27 @@

    Index

  • jumpscale.tools.codeloader
  • jumpscale.tools.console
  • jumpscale.tools.depsresolver
  • +
  • jumpscale.tools.dnstool
  • jumpscale.tools.errorhandler
  • +
  • jumpscale.tools.export
  • jumpscale.tools.git
  • jumpscale.tools.highlighter
  • jumpscale.tools.http
  • jumpscale.tools.jinja2
  • jumpscale.tools.keygen
  • +
  • jumpscale.tools.nginx
  • +
  • jumpscale.tools.notificationsqueue
  • +
  • jumpscale.tools.poolexecutor
  • +
  • jumpscale.tools.qrcode
  • +
  • jumpscale.tools.redis
  • +
  • jumpscale.tools.restic
  • jumpscale.tools.schemac
  • +
  • jumpscale.tools.servicemanager
  • jumpscale.tools.startupcmd
  • jumpscale.tools.syncer
  • +
  • jumpscale.tools.tfgateway
  • jumpscale.tools.timer
  • +
  • jumpscale.tools.wireguard
  • @@ -132,4 +187,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/jinja2/index.html b/docs/api/jumpscale/tools/jinja2/index.html index 4810fc8b4..eeba33367 100644 --- a/docs/api/jumpscale/tools/jinja2/index.html +++ b/docs/api/jumpscale/tools/jinja2/index.html @@ -375,4 +375,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/keygen/index.html b/docs/api/jumpscale/tools/keygen/index.html index a715d7102..cdfc77cf7 100644 --- a/docs/api/jumpscale/tools/keygen/index.html +++ b/docs/api/jumpscale/tools/keygen/index.html @@ -62,4 +62,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/keygen/keygen.html b/docs/api/jumpscale/tools/keygen/keygen.html index 0134df051..d62a51ffa 100644 --- a/docs/api/jumpscale/tools/keygen/keygen.html +++ b/docs/api/jumpscale/tools/keygen/keygen.html @@ -56,4 +56,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/nginx/index.html b/docs/api/jumpscale/tools/nginx/index.html new file mode 100644 index 000000000..c0f789cc0 --- /dev/null +++ b/docs/api/jumpscale/tools/nginx/index.html @@ -0,0 +1,169 @@ + + + + + + +jumpscale.tools.nginx API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.nginx

    +
    +
    +

    Nginx tool

    +

    This tool is help for (install, start, stop, reload, restart) Nginx server.

    +

    NGINX is open source software for web serving, reverse proxying, caching, load balancing, media streaming, and more. +It started out as a web server designed for maximum performance and stability. In addition to its HTTP server capabilities, +NGINX can also function as a proxy server for email (IMAP, POP3, and SMTP) and a reverse proxy and load balancer for HTTP, TCP, and UDP servers.

    +

    Install

    +
    main = j.tools.nginx.get(name="main")
    +main.install()
    +
    +

    Start

    +
    main = j.tools.nginx.get(name="main")
    +main.start()
    +
    +

    Stop

    +
    main = j.tools.nginx.get(name="main")
    +main.stop()
    +
    +

    reload

    +
    main = j.tools.nginx.get(name="main")
    +main.reload()
    +
    +

    restart

    +
    main = j.tools.nginx.get(name="main")
    +main.restart()
    +
    +
    + +Expand source code + +
    '''
    +# Nginx tool
    +
    +This tool is help for (install, start, stop, reload, restart) Nginx server.
    +
    +NGINX is open source software for web serving, reverse proxying, caching, load balancing, media streaming, and more.
    +It started out as a web server designed for maximum performance and stability. In addition to its HTTP server capabilities,
    +NGINX can also function as a proxy server for email (IMAP, POP3, and SMTP) and a reverse proxy and load balancer for HTTP, TCP, and UDP servers.
    +
    +## Install
    +```
    +main = j.tools.nginx.get(name="main")
    +main.install()
    +```
    +## Start
    +```
    +main = j.tools.nginx.get(name="main")
    +main.start()
    +```
    +## Stop
    +```
    +main = j.tools.nginx.get(name="main")
    +main.stop()
    +```
    +## reload
    +```
    +main = j.tools.nginx.get(name="main")
    +main.reload()
    +```
    +## restart
    +```
    +main = j.tools.nginx.get(name="main")
    +main.restart()
    +```
    +'''
    +
    +def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .nginxserver import NginxServer
    +
    +    return StoredFactory(NginxServer)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.tools.nginx.nginxserver
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .nginxserver import NginxServer
    +
    +    return StoredFactory(NginxServer)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/nginx/nginxserver.html b/docs/api/jumpscale/tools/nginx/nginxserver.html new file mode 100644 index 000000000..061bc9424 --- /dev/null +++ b/docs/api/jumpscale/tools/nginx/nginxserver.html @@ -0,0 +1,508 @@ + + + + + + +jumpscale.tools.nginx.nginxserver API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.nginx.nginxserver

    +
    +
    +
    + +Expand source code + +
    import re
    +import shutil
    +from jumpscale.loader import j
    +from jumpscale.core.base import Base, fields
    +
    +
    +class NginxServer(Base):
    +    server_name = fields.String(default="main")
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.config_path = j.sals.fs.join_paths(j.core.dirs.CFGDIR, "nginx", self.server_name, "nginx.conf")
    +
    +    @property
    +    def check_command_string(self):
    +        return r"nginx.* \-c {CONFIG_PATH}".format(CONFIG_PATH=j.sals.fs.expanduser(self.config_path))
    +
    +    @property
    +    def installed(self) -> bool:
    +        """check if nginx is installed
    +
    +        Returns:
    +            bool: True if nginx is installed
    +        """
    +        return shutil.which("nginx")
    +
    +    def start(self):
    +        """
    +        start nginx server using your config path
    +        """
    +        nginx = j.sals.nginx.get(self.server_name)
    +        nginx.configure()
    +        cmd = j.tools.startupcmd.get(f"nginx_{self.server_name}")
    +        cmd.start_cmd = f"nginx -c {self.config_path}"
    +        cmd.stop_cmd = f"nginx -c {self.config_path} -s stop"
    +        cmd.path = nginx.cfg_dir
    +        cmd.timeout = 10
    +        pid_file_path = self.get_pid_file_path()
    +        cmd.check_cmd = f'pid=$(cat "{pid_file_path}") && [[ $(ps -p $pid -o command=) == "nginx: master"* ]]'
    +        cmd.process_strings_regex = [self.check_command_string]
    +        cmd.save()
    +        if not cmd.is_running():
    +            cmd.start()
    +
    +    def stop(self):
    +        """
    +        stop nginx server
    +        """
    +        cmd = j.tools.startupcmd.get(f"nginx_{self.server_name}")
    +        cmd.stop()
    +
    +    def is_running(self):
    +        """Check if nginxserver is running
    +
    +        Returns:
    +            bool: True if Nginx master process is running, otherwise False.
    +        """
    +        return j.tools.startupcmd.get(f"nginx_{self.server_name}").is_running()
    +
    +    def reload(self):
    +        """
    +        reload nginx server using your config path
    +        """
    +        j.sals.process.execute(f"nginx -c {self.config_path} -s reload")
    +
    +    def restart(self):
    +        """
    +        restart nginx server
    +        """
    +        self.stop()
    +        self.start()
    +
    +    def get_pid_file_path(self):
    +        """Return the path to nginx.pid file as specified in the Nginx configuration file
    +        Raises:
    +            j.exceptions.Runtime: if pid directive not found in the Nginx configuration file
    +            FileNotFoundError: if the Nginx configuration file not found
    +        Returns:
    +            str: the path to the file that process ID of the Nginx master process is written to.
    +        """
    +        with open(j.sals.fs.expanduser(self.config_path), "r") as file:
    +            for line in file:
    +                m = re.match(r"^pid\s+(.*);", line)
    +                if m:
    +                    pid_file_path = m.group(1)
    +                    return pid_file_path
    +            else:
    +                raise j.exceptions.Runtime(
    +                    f"can't read the PID directive in the Nginx configuration file {self.config_path}"
    +                )
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class NginxServer +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class NginxServer(Base):
    +    server_name = fields.String(default="main")
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.config_path = j.sals.fs.join_paths(j.core.dirs.CFGDIR, "nginx", self.server_name, "nginx.conf")
    +
    +    @property
    +    def check_command_string(self):
    +        return r"nginx.* \-c {CONFIG_PATH}".format(CONFIG_PATH=j.sals.fs.expanduser(self.config_path))
    +
    +    @property
    +    def installed(self) -> bool:
    +        """check if nginx is installed
    +
    +        Returns:
    +            bool: True if nginx is installed
    +        """
    +        return shutil.which("nginx")
    +
    +    def start(self):
    +        """
    +        start nginx server using your config path
    +        """
    +        nginx = j.sals.nginx.get(self.server_name)
    +        nginx.configure()
    +        cmd = j.tools.startupcmd.get(f"nginx_{self.server_name}")
    +        cmd.start_cmd = f"nginx -c {self.config_path}"
    +        cmd.stop_cmd = f"nginx -c {self.config_path} -s stop"
    +        cmd.path = nginx.cfg_dir
    +        cmd.timeout = 10
    +        pid_file_path = self.get_pid_file_path()
    +        cmd.check_cmd = f'pid=$(cat "{pid_file_path}") && [[ $(ps -p $pid -o command=) == "nginx: master"* ]]'
    +        cmd.process_strings_regex = [self.check_command_string]
    +        cmd.save()
    +        if not cmd.is_running():
    +            cmd.start()
    +
    +    def stop(self):
    +        """
    +        stop nginx server
    +        """
    +        cmd = j.tools.startupcmd.get(f"nginx_{self.server_name}")
    +        cmd.stop()
    +
    +    def is_running(self):
    +        """Check if nginxserver is running
    +
    +        Returns:
    +            bool: True if Nginx master process is running, otherwise False.
    +        """
    +        return j.tools.startupcmd.get(f"nginx_{self.server_name}").is_running()
    +
    +    def reload(self):
    +        """
    +        reload nginx server using your config path
    +        """
    +        j.sals.process.execute(f"nginx -c {self.config_path} -s reload")
    +
    +    def restart(self):
    +        """
    +        restart nginx server
    +        """
    +        self.stop()
    +        self.start()
    +
    +    def get_pid_file_path(self):
    +        """Return the path to nginx.pid file as specified in the Nginx configuration file
    +        Raises:
    +            j.exceptions.Runtime: if pid directive not found in the Nginx configuration file
    +            FileNotFoundError: if the Nginx configuration file not found
    +        Returns:
    +            str: the path to the file that process ID of the Nginx master process is written to.
    +        """
    +        with open(j.sals.fs.expanduser(self.config_path), "r") as file:
    +            for line in file:
    +                m = re.match(r"^pid\s+(.*);", line)
    +                if m:
    +                    pid_file_path = m.group(1)
    +                    return pid_file_path
    +            else:
    +                raise j.exceptions.Runtime(
    +                    f"can't read the PID directive in the Nginx configuration file {self.config_path}"
    +                )
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var check_command_string
    +
    +
    +
    + +Expand source code + +
    @property
    +def check_command_string(self):
    +    return r"nginx.* \-c {CONFIG_PATH}".format(CONFIG_PATH=j.sals.fs.expanduser(self.config_path))
    +
    +
    +
    var installed : bool
    +
    +

    check if nginx is installed

    +

    Returns

    +
    +
    bool
    +
    True if nginx is installed
    +
    +
    + +Expand source code + +
    @property
    +def installed(self) -> bool:
    +    """check if nginx is installed
    +
    +    Returns:
    +        bool: True if nginx is installed
    +    """
    +    return shutil.which("nginx")
    +
    +
    +
    var server_name
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def get_pid_file_path(self) +
    +
    +

    Return the path to nginx.pid file as specified in the Nginx configuration file

    +

    Raises

    +
    +
    j.exceptions.Runtime
    +
    if pid directive not found in the Nginx configuration file
    +
    FileNotFoundError
    +
    if the Nginx configuration file not found
    +
    +

    Returns

    +
    +
    str
    +
    the path to the file that process ID of the Nginx master process is written to.
    +
    +
    + +Expand source code + +
    def get_pid_file_path(self):
    +    """Return the path to nginx.pid file as specified in the Nginx configuration file
    +    Raises:
    +        j.exceptions.Runtime: if pid directive not found in the Nginx configuration file
    +        FileNotFoundError: if the Nginx configuration file not found
    +    Returns:
    +        str: the path to the file that process ID of the Nginx master process is written to.
    +    """
    +    with open(j.sals.fs.expanduser(self.config_path), "r") as file:
    +        for line in file:
    +            m = re.match(r"^pid\s+(.*);", line)
    +            if m:
    +                pid_file_path = m.group(1)
    +                return pid_file_path
    +        else:
    +            raise j.exceptions.Runtime(
    +                f"can't read the PID directive in the Nginx configuration file {self.config_path}"
    +            )
    +
    +
    +
    +def is_running(self) +
    +
    +

    Check if nginxserver is running

    +

    Returns

    +
    +
    bool
    +
    True if Nginx master process is running, otherwise False.
    +
    +
    + +Expand source code + +
    def is_running(self):
    +    """Check if nginxserver is running
    +
    +    Returns:
    +        bool: True if Nginx master process is running, otherwise False.
    +    """
    +    return j.tools.startupcmd.get(f"nginx_{self.server_name}").is_running()
    +
    +
    +
    +def reload(self) +
    +
    +

    reload nginx server using your config path

    +
    + +Expand source code + +
    def reload(self):
    +    """
    +    reload nginx server using your config path
    +    """
    +    j.sals.process.execute(f"nginx -c {self.config_path} -s reload")
    +
    +
    +
    +def restart(self) +
    +
    +

    restart nginx server

    +
    + +Expand source code + +
    def restart(self):
    +    """
    +    restart nginx server
    +    """
    +    self.stop()
    +    self.start()
    +
    +
    +
    +def start(self) +
    +
    +

    start nginx server using your config path

    +
    + +Expand source code + +
    def start(self):
    +    """
    +    start nginx server using your config path
    +    """
    +    nginx = j.sals.nginx.get(self.server_name)
    +    nginx.configure()
    +    cmd = j.tools.startupcmd.get(f"nginx_{self.server_name}")
    +    cmd.start_cmd = f"nginx -c {self.config_path}"
    +    cmd.stop_cmd = f"nginx -c {self.config_path} -s stop"
    +    cmd.path = nginx.cfg_dir
    +    cmd.timeout = 10
    +    pid_file_path = self.get_pid_file_path()
    +    cmd.check_cmd = f'pid=$(cat "{pid_file_path}") && [[ $(ps -p $pid -o command=) == "nginx: master"* ]]'
    +    cmd.process_strings_regex = [self.check_command_string]
    +    cmd.save()
    +    if not cmd.is_running():
    +        cmd.start()
    +
    +
    +
    +def stop(self) +
    +
    +

    stop nginx server

    +
    + +Expand source code + +
    def stop(self):
    +    """
    +    stop nginx server
    +    """
    +    cmd = j.tools.startupcmd.get(f"nginx_{self.server_name}")
    +    cmd.stop()
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/notificationsqueue/index.html b/docs/api/jumpscale/tools/notificationsqueue/index.html new file mode 100644 index 000000000..098ba25bf --- /dev/null +++ b/docs/api/jumpscale/tools/notificationsqueue/index.html @@ -0,0 +1,97 @@ + + + + + + +jumpscale.tools.notificationsqueue API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.notificationsqueue

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from .queue import NotificationsQueue
    +
    +    return NotificationsQueue()
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.tools.notificationsqueue.queue
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from .queue import NotificationsQueue
    +
    +    return NotificationsQueue()
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/notificationsqueue/queue.html b/docs/api/jumpscale/tools/notificationsqueue/queue.html new file mode 100644 index 000000000..32eb049a2 --- /dev/null +++ b/docs/api/jumpscale/tools/notificationsqueue/queue.html @@ -0,0 +1,578 @@ + + + + + + +jumpscale.tools.notificationsqueue.queue API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.notificationsqueue.queue

    +
    +
    +
    + +Expand source code + +
    from enum import Enum
    +
    +from jumpscale.loader import j
    +
    +
    +class LEVEL(Enum):
    +    INFO = "info"
    +    WARNING = "warning"
    +    ERROR = "error"
    +
    +
    +class Notification:
    +    def __init__(self):
    +        self.id = None
    +        self.category = None
    +        self.level = 0
    +        self.message = None
    +        self.date = None
    +
    +    @classmethod
    +    def loads(cls, value):
    +        json = j.data.serializers.json.loads(value)
    +        instance = cls()
    +        instance.__dict__ = json
    +        return instance
    +
    +    @property
    +    def json(self):
    +        return self.__dict__
    +
    +    def dumps(self):
    +        return j.data.serializers.json.dumps(self.__dict__)
    +
    +
    +class NotificationsQueue:
    +    def __init__(self, *args, **kwargs):
    +        self._rkey = "queue:notifications"
    +        self._rkey_incr = "queue:notifications:id:incr"
    +        self._rkey_seen = "list:notifications:seen"
    +        self._seen_list_max_size = 10
    +        self._db = None
    +
    +    @property
    +    def db(self):
    +        if self._db is None:
    +            self._db = j.core.db
    +        return self._db
    +
    +    def push(self, message: str, category: str = "default", level: LEVEL = LEVEL.INFO):
    +        """Push a new notification
    +
    +        Arguments:
    +            message {str}: notification message
    +
    +        Keyword Arguments:
    +            type {str}: notification type (default: {"default"})
    +            level {LEVEL}: notification level (default: {LEVEL.INFO})
    +        """
    +        if not self.db.is_running():
    +            return
    +
    +        if not isinstance(level, LEVEL):
    +            raise j.exceptions.Value(f"{level} is not of type: LEVEL")
    +
    +        notification = Notification()
    +
    +        notification.message = message
    +        notification.level = level.value
    +        notification.category = category
    +        notification.date = j.data.time.utcnow().timestamp
    +        notification.id = self.db.incr(self._rkey_incr)
    +
    +        self.db.lpush(self._rkey, notification.dumps())
    +
    +    def fetch(self, count: int = -1) -> list:
    +        """Fetch notifications from queue
    +
    +        Keyword Arguments:
    +            count {int}: number of new notifications to fetch (default: {-1} = new notifications only)
    +                           if new notifications < count => will return list of (new notifications + old notifications) with length = count
    +
    +        Returns:
    +            list: Notification objects
    +        """
    +        get_all_new = count == -1
    +        ret_notifications_count = 0
    +
    +        if get_all_new:
    +            ret_notifications_count = self.count()
    +            if ret_notifications_count == 0:
    +                return []
    +        else:
    +            ret_notifications_count = count
    +
    +        # Transactional pipeline to fetch notifications from the queue and save them in the seen list
    +        p = self.db.pipeline()
    +        p.multi()
    +
    +        for _ in range(ret_notifications_count):
    +            p.rpoplpush(self._rkey, self._rkey_seen)
    +        p.lrange(self._rkey_seen, 0, ret_notifications_count - 1)
    +        p.ltrim(self._rkey_seen, 0, self._seen_list_max_size - 1)
    +
    +        notifications = p.execute()
    +
    +        return [Notification.loads(notification) for notification in notifications[-2]]  # -2 = result of lrange command
    +
    +    def count(self) -> int:
    +        """Get notifications count
    +
    +        Returns:
    +            int: total number of notifications
    +        """
    +        return self.db.llen(self._rkey)
    +
    +    def clear(self):
    +        """Delete all notifications
    +        """
    +        self.db.delete(self._rkey)
    +        self.db.delete(self._rkey_seen)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class LEVEL +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
    +
    +

    An enumeration.

    +
    + +Expand source code + +
    class LEVEL(Enum):
    +    INFO = "info"
    +    WARNING = "warning"
    +    ERROR = "error"
    +
    +

    Ancestors

    +
      +
    • enum.Enum
    • +
    +

    Class variables

    +
    +
    var ERROR
    +
    +
    +
    +
    var INFO
    +
    +
    +
    +
    var WARNING
    +
    +
    +
    +
    +
    +
    +class Notification +
    +
    +
    +
    + +Expand source code + +
    class Notification:
    +    def __init__(self):
    +        self.id = None
    +        self.category = None
    +        self.level = 0
    +        self.message = None
    +        self.date = None
    +
    +    @classmethod
    +    def loads(cls, value):
    +        json = j.data.serializers.json.loads(value)
    +        instance = cls()
    +        instance.__dict__ = json
    +        return instance
    +
    +    @property
    +    def json(self):
    +        return self.__dict__
    +
    +    def dumps(self):
    +        return j.data.serializers.json.dumps(self.__dict__)
    +
    +

    Static methods

    +
    +
    +def loads(value) +
    +
    +
    +
    + +Expand source code + +
    @classmethod
    +def loads(cls, value):
    +    json = j.data.serializers.json.loads(value)
    +    instance = cls()
    +    instance.__dict__ = json
    +    return instance
    +
    +
    +
    +

    Instance variables

    +
    +
    var json
    +
    +
    +
    + +Expand source code + +
    @property
    +def json(self):
    +    return self.__dict__
    +
    +
    +
    +

    Methods

    +
    +
    +def dumps(self) +
    +
    +
    +
    + +Expand source code + +
    def dumps(self):
    +    return j.data.serializers.json.dumps(self.__dict__)
    +
    +
    +
    +
    +
    +class NotificationsQueue +(*args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    class NotificationsQueue:
    +    def __init__(self, *args, **kwargs):
    +        self._rkey = "queue:notifications"
    +        self._rkey_incr = "queue:notifications:id:incr"
    +        self._rkey_seen = "list:notifications:seen"
    +        self._seen_list_max_size = 10
    +        self._db = None
    +
    +    @property
    +    def db(self):
    +        if self._db is None:
    +            self._db = j.core.db
    +        return self._db
    +
    +    def push(self, message: str, category: str = "default", level: LEVEL = LEVEL.INFO):
    +        """Push a new notification
    +
    +        Arguments:
    +            message {str}: notification message
    +
    +        Keyword Arguments:
    +            type {str}: notification type (default: {"default"})
    +            level {LEVEL}: notification level (default: {LEVEL.INFO})
    +        """
    +        if not self.db.is_running():
    +            return
    +
    +        if not isinstance(level, LEVEL):
    +            raise j.exceptions.Value(f"{level} is not of type: LEVEL")
    +
    +        notification = Notification()
    +
    +        notification.message = message
    +        notification.level = level.value
    +        notification.category = category
    +        notification.date = j.data.time.utcnow().timestamp
    +        notification.id = self.db.incr(self._rkey_incr)
    +
    +        self.db.lpush(self._rkey, notification.dumps())
    +
    +    def fetch(self, count: int = -1) -> list:
    +        """Fetch notifications from queue
    +
    +        Keyword Arguments:
    +            count {int}: number of new notifications to fetch (default: {-1} = new notifications only)
    +                           if new notifications < count => will return list of (new notifications + old notifications) with length = count
    +
    +        Returns:
    +            list: Notification objects
    +        """
    +        get_all_new = count == -1
    +        ret_notifications_count = 0
    +
    +        if get_all_new:
    +            ret_notifications_count = self.count()
    +            if ret_notifications_count == 0:
    +                return []
    +        else:
    +            ret_notifications_count = count
    +
    +        # Transactional pipeline to fetch notifications from the queue and save them in the seen list
    +        p = self.db.pipeline()
    +        p.multi()
    +
    +        for _ in range(ret_notifications_count):
    +            p.rpoplpush(self._rkey, self._rkey_seen)
    +        p.lrange(self._rkey_seen, 0, ret_notifications_count - 1)
    +        p.ltrim(self._rkey_seen, 0, self._seen_list_max_size - 1)
    +
    +        notifications = p.execute()
    +
    +        return [Notification.loads(notification) for notification in notifications[-2]]  # -2 = result of lrange command
    +
    +    def count(self) -> int:
    +        """Get notifications count
    +
    +        Returns:
    +            int: total number of notifications
    +        """
    +        return self.db.llen(self._rkey)
    +
    +    def clear(self):
    +        """Delete all notifications
    +        """
    +        self.db.delete(self._rkey)
    +        self.db.delete(self._rkey_seen)
    +
    +

    Instance variables

    +
    +
    var db
    +
    +
    +
    + +Expand source code + +
    @property
    +def db(self):
    +    if self._db is None:
    +        self._db = j.core.db
    +    return self._db
    +
    +
    +
    +

    Methods

    +
    +
    +def clear(self) +
    +
    +

    Delete all notifications

    +
    + +Expand source code + +
    def clear(self):
    +    """Delete all notifications
    +    """
    +    self.db.delete(self._rkey)
    +    self.db.delete(self._rkey_seen)
    +
    +
    +
    +def count(self) ‑> int +
    +
    +

    Get notifications count

    +

    Returns

    +
    +
    int
    +
    total number of notifications
    +
    +
    + +Expand source code + +
    def count(self) -> int:
    +    """Get notifications count
    +
    +    Returns:
    +        int: total number of notifications
    +    """
    +    return self.db.llen(self._rkey)
    +
    +
    +
    +def fetch(self, count: int = -1) ‑> list +
    +
    +

    Fetch notifications from queue

    +

    Keyword Arguments: +count {int}: number of new notifications to fetch (default: {-1} = new notifications only) +if new notifications < count => will return list of (new notifications + old notifications) with length = count

    +

    Returns

    +
    +
    list
    +
    Notification objects
    +
    +
    + +Expand source code + +
    def fetch(self, count: int = -1) -> list:
    +    """Fetch notifications from queue
    +
    +    Keyword Arguments:
    +        count {int}: number of new notifications to fetch (default: {-1} = new notifications only)
    +                       if new notifications < count => will return list of (new notifications + old notifications) with length = count
    +
    +    Returns:
    +        list: Notification objects
    +    """
    +    get_all_new = count == -1
    +    ret_notifications_count = 0
    +
    +    if get_all_new:
    +        ret_notifications_count = self.count()
    +        if ret_notifications_count == 0:
    +            return []
    +    else:
    +        ret_notifications_count = count
    +
    +    # Transactional pipeline to fetch notifications from the queue and save them in the seen list
    +    p = self.db.pipeline()
    +    p.multi()
    +
    +    for _ in range(ret_notifications_count):
    +        p.rpoplpush(self._rkey, self._rkey_seen)
    +    p.lrange(self._rkey_seen, 0, ret_notifications_count - 1)
    +    p.ltrim(self._rkey_seen, 0, self._seen_list_max_size - 1)
    +
    +    notifications = p.execute()
    +
    +    return [Notification.loads(notification) for notification in notifications[-2]]  # -2 = result of lrange command
    +
    +
    +
    +def push(self, message: str, category: str = 'default', level: LEVEL = LEVEL.INFO) +
    +
    +

    Push a new notification

    +

    Arguments

    +

    message {str}: notification message

    +

    Keyword Arguments: +type {str}: notification type (default: {"default"}) +level {LEVEL}: notification level (default: {LEVEL.INFO})

    +
    + +Expand source code + +
    def push(self, message: str, category: str = "default", level: LEVEL = LEVEL.INFO):
    +    """Push a new notification
    +
    +    Arguments:
    +        message {str}: notification message
    +
    +    Keyword Arguments:
    +        type {str}: notification type (default: {"default"})
    +        level {LEVEL}: notification level (default: {LEVEL.INFO})
    +    """
    +    if not self.db.is_running():
    +        return
    +
    +    if not isinstance(level, LEVEL):
    +        raise j.exceptions.Value(f"{level} is not of type: LEVEL")
    +
    +    notification = Notification()
    +
    +    notification.message = message
    +    notification.level = level.value
    +    notification.category = category
    +    notification.date = j.data.time.utcnow().timestamp
    +    notification.id = self.db.incr(self._rkey_incr)
    +
    +    self.db.lpush(self._rkey, notification.dumps())
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/poolexecutor/index.html b/docs/api/jumpscale/tools/poolexecutor/index.html new file mode 100644 index 000000000..fa0c1e281 --- /dev/null +++ b/docs/api/jumpscale/tools/poolexecutor/index.html @@ -0,0 +1,89 @@ + + + + + + +jumpscale.tools.poolexecutor API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.poolexecutor

    +
    +
    +
    + +Expand source code + +
    from .poolexecutor import PoolExecutor
    +
    +"""
    +
    +def sleepf(howlong, name="fun"):
    +    print("{} is sleeping for {}".format(name, howlong))
    +    for i in range(howlong):
    +        print("{} is sleeping slept for {}".format(name, howlong - i))
    +        gevent.sleep(i)
    +
    +with j.tools.poolexecutor.PoolExecutor() as p:
    +    for i in range(5):
    +        p.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    gs = p.run()
    +    print(p.results(gs))
    +
    +
    +"""
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.tools.poolexecutor.poolexecutor
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/poolexecutor/poolexecutor.html b/docs/api/jumpscale/tools/poolexecutor/poolexecutor.html new file mode 100644 index 000000000..0684badc3 --- /dev/null +++ b/docs/api/jumpscale/tools/poolexecutor/poolexecutor.html @@ -0,0 +1,351 @@ + + + + + + +jumpscale.tools.poolexecutor.poolexecutor API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.poolexecutor.poolexecutor

    +
    +
    +
    + +Expand source code + +
    import gevent
    +
    +
    +class Job:
    +    def __init__(self, fun, *args, **kwargs):
    +        self.fun = fun
    +        self.args = args
    +        self.kwargs = kwargs
    +
    +
    +class PoolExecutor:
    +    def __init__(self):
    +        self.jobs = []
    +    
    +    def __enter__(self):
    +        return self
    +
    +    def __exit__(self, type, value, tb):
    +        pass
    +
    +    def task_add(self, fun, *args, **kwargs):
    +        self.jobs.append(Job(fun, *args, **kwargs))
    +
    +    def run(self, die=True):
    +        try:
    +            greenlets = [gevent.spawn(job.fun, *job.args, **job.kwargs) for job in self.jobs]
    +            # print("greenlets: ", greenlets)
    +            gevent.joinall(greenlets, raise_error=die)
    +        except Exception as e:
    +            self.jobs = []
    +            raise e
    +        else:
    +            self.jobs = []
    +            return greenlets
    +    
    +    def results(self, greenlets):
    +        return [greenlet.value for greenlet in greenlets]
    +
    +
    +    # def test_simple(self):
    +    #     with j.tools.poolexecutor.PoolExecutor() as p:
    +    #         for i in range(5):
    +    #             p.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    #         gs = p.run()
    +    #         p.results(gs)
    +    
    +    #     def sleepf(howlong, name="fun"):
    +    #         print("{} is sleeping for {}".format(name, howlong))
    +    #         for i in range(howlong):
    +    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    +    #             gevent.sleep(i)
    +
    +    #     for i in range(5):
    +    #         self.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    #     self.run()
    +
    +    # def test_with_errors(self):
    +
    +    #     def sleepf(howlong, name="fun"):
    +    #         print("{} is sleeping for {}".format(name, howlong))
    +    #         for i in range(howlong):
    +    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    +    #             gevent.sleep(i)
    +
    +    #     def sleepf_with_error(howlong, name="fun"):
    +    #         print("{} is sleeping for {}".format(name, howlong))
    +    #         for i in range(howlong):
    +    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    +    #             gevent.sleep(i)
    +    #         raise RuntimeError("error here in sleepf_with_error")
    +
    +    #     for i in range(5):
    +    #         self.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    #     self.task_add(sleepf_with_error, i, name="error_fun")
    +
    +    #     try:
    +    #         self.run()
    +    #     except:
    +    #         print("run has a function that raises and we caught it.")
    +
    +    # def test_with_results(self):
    +
    +    #     def sleepf(howlong, name="fun"):
    +    #         print("{} is sleeping for {}".format(name, howlong))
    +    #         for i in range(howlong):
    +    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    +    #             gevent.sleep(i)
    +    #         return 7
    +
    +    #     for i in range(5):
    +    #         self.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    #     greenlets = self.run()
    +    #     results = [greenlet.value for greenlet in greenlets]
    +    #     assert all(map(lambda x: x == 7, results)) == True
    +    #     print(results)
    +
    +    # def test(self):
    +    #     for f in [self.test_simple, self.test_with_results, self.test_with_errors]:
    +    #         f()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Job +(fun, *args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    class Job:
    +    def __init__(self, fun, *args, **kwargs):
    +        self.fun = fun
    +        self.args = args
    +        self.kwargs = kwargs
    +
    +
    +
    +class PoolExecutor +
    +
    +
    +
    + +Expand source code + +
    class PoolExecutor:
    +    def __init__(self):
    +        self.jobs = []
    +    
    +    def __enter__(self):
    +        return self
    +
    +    def __exit__(self, type, value, tb):
    +        pass
    +
    +    def task_add(self, fun, *args, **kwargs):
    +        self.jobs.append(Job(fun, *args, **kwargs))
    +
    +    def run(self, die=True):
    +        try:
    +            greenlets = [gevent.spawn(job.fun, *job.args, **job.kwargs) for job in self.jobs]
    +            # print("greenlets: ", greenlets)
    +            gevent.joinall(greenlets, raise_error=die)
    +        except Exception as e:
    +            self.jobs = []
    +            raise e
    +        else:
    +            self.jobs = []
    +            return greenlets
    +    
    +    def results(self, greenlets):
    +        return [greenlet.value for greenlet in greenlets]
    +
    +
    +    # def test_simple(self):
    +    #     with j.tools.poolexecutor.PoolExecutor() as p:
    +    #         for i in range(5):
    +    #             p.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    #         gs = p.run()
    +    #         p.results(gs)
    +    
    +    #     def sleepf(howlong, name="fun"):
    +    #         print("{} is sleeping for {}".format(name, howlong))
    +    #         for i in range(howlong):
    +    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    +    #             gevent.sleep(i)
    +
    +    #     for i in range(5):
    +    #         self.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    #     self.run()
    +
    +    # def test_with_errors(self):
    +
    +    #     def sleepf(howlong, name="fun"):
    +    #         print("{} is sleeping for {}".format(name, howlong))
    +    #         for i in range(howlong):
    +    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    +    #             gevent.sleep(i)
    +
    +    #     def sleepf_with_error(howlong, name="fun"):
    +    #         print("{} is sleeping for {}".format(name, howlong))
    +    #         for i in range(howlong):
    +    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    +    #             gevent.sleep(i)
    +    #         raise RuntimeError("error here in sleepf_with_error")
    +
    +    #     for i in range(5):
    +    #         self.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    #     self.task_add(sleepf_with_error, i, name="error_fun")
    +
    +    #     try:
    +    #         self.run()
    +    #     except:
    +    #         print("run has a function that raises and we caught it.")
    +
    +    # def test_with_results(self):
    +
    +    #     def sleepf(howlong, name="fun"):
    +    #         print("{} is sleeping for {}".format(name, howlong))
    +    #         for i in range(howlong):
    +    #             print("{} is sleeping slept for {}".format(name, howlong - i))
    +    #             gevent.sleep(i)
    +    #         return 7
    +
    +    #     for i in range(5):
    +    #         self.task_add(sleepf, i, name="fun{}".format(i))
    +
    +    #     greenlets = self.run()
    +    #     results = [greenlet.value for greenlet in greenlets]
    +    #     assert all(map(lambda x: x == 7, results)) == True
    +    #     print(results)
    +
    +    # def test(self):
    +    #     for f in [self.test_simple, self.test_with_results, self.test_with_errors]:
    +    #         f()
    +
    +

    Methods

    +
    +
    +def results(self, greenlets) +
    +
    +
    +
    + +Expand source code + +
    def results(self, greenlets):
    +    return [greenlet.value for greenlet in greenlets]
    +
    +
    +
    +def run(self, die=True) +
    +
    +
    +
    + +Expand source code + +
    def run(self, die=True):
    +    try:
    +        greenlets = [gevent.spawn(job.fun, *job.args, **job.kwargs) for job in self.jobs]
    +        # print("greenlets: ", greenlets)
    +        gevent.joinall(greenlets, raise_error=die)
    +    except Exception as e:
    +        self.jobs = []
    +        raise e
    +    else:
    +        self.jobs = []
    +        return greenlets
    +
    +
    +
    +def task_add(self, fun, *args, **kwargs) +
    +
    +
    +
    + +Expand source code + +
    def task_add(self, fun, *args, **kwargs):
    +    self.jobs.append(Job(fun, *args, **kwargs))
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/qrcode/index.html b/docs/api/jumpscale/tools/qrcode/index.html new file mode 100644 index 000000000..89c6d5381 --- /dev/null +++ b/docs/api/jumpscale/tools/qrcode/index.html @@ -0,0 +1,235 @@ + + + + + + +jumpscale.tools.qrcode API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.qrcode

    +
    +
    +
    + +Expand source code + +
    import pyqrcode
    +
    +
    +def _get_qr_object(content, version, mode, encoding):
    +    return pyqrcode.create(content, version=version, mode=mode, encoding=encoding)
    +
    +
    +def png_get(content, file_path, version=None, mode=None, encoding=None, scale=1):
    +    """Writes QR code to file in PNG format
    +
    +    Args:
    +        content (str): content to be encoded
    +        file_path (str): file path to write to
    +        version (int, optional): data capacity of the code, automatic if not specified. Defaults to None.
    +        mode (str, optional): how the content will be encoded, automatic if not specified. Defaults to None.
    +        encoding (str, optional): Encoding of the specfied content string. Defaults to None.
    +        scale (int, optional): scale of the QR code relative to the module. Defaults to 1.
    +    """
    +    qr_object = _get_qr_object(content, version, mode, encoding)
    +    qr_object.png(file_path, scale=scale)
    +
    +
    +def svg_get(content, file_path, version=None, mode=None, encoding=None, scale=1, title=None):
    +    """Writes QR code to file in SVG format
    +
    +    Args:
    +        content (str): content to be encoded
    +        file_path (str): file path to write to
    +        version (int, optional): data capacity of the code, automatic if not specified. Defaults to None.
    +        mode (str, optional): how the content will be encoded, automatic if not specified. Defaults to None.
    +        encoding (str, optional): Encoding of the specfied content string. Defaults to None.
    +        scale (int, optional): scale of the QR code relative to the module. Defaults to 1.
    +        title (str, optional): title of the SVG. Defauls to None.
    +    """
    +    qr_object = _get_qr_object(content, version, mode, encoding)
    +    qr_object.svg(file_path, scale=scale, title=title)
    +
    +
    +def base64_get(content, version=None, mode=None, encoding=None, scale=1):
    +    """returns base 64 of QR code
    +
    +    Args:
    +        content (str): content to be encoded
    +        version (int, optional): data capacity of the code, automatic if not specified. Defaults to None.
    +        mode (str, optional): how the content will be encoded, automatic if not specified. Defaults to None.
    +        encoding (str, optional): Encoding of the specfied content string. Defaults to None.
    +        scale (int, optional): scale of the QR code relative to the module. Defaults to 1.
    +    """
    +    qr_object = _get_qr_object(content, version, mode, encoding)
    +    return qr_object.png_as_base64_str(scale=scale)
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def base64_get(content, version=None, mode=None, encoding=None, scale=1) +
    +
    +

    returns base 64 of QR code

    +

    Args

    +
    +
    content : str
    +
    content to be encoded
    +
    version : int, optional
    +
    data capacity of the code, automatic if not specified. Defaults to None.
    +
    mode : str, optional
    +
    how the content will be encoded, automatic if not specified. Defaults to None.
    +
    encoding : str, optional
    +
    Encoding of the specfied content string. Defaults to None.
    +
    scale : int, optional
    +
    scale of the QR code relative to the module. Defaults to 1.
    +
    +
    + +Expand source code + +
    def base64_get(content, version=None, mode=None, encoding=None, scale=1):
    +    """returns base 64 of QR code
    +
    +    Args:
    +        content (str): content to be encoded
    +        version (int, optional): data capacity of the code, automatic if not specified. Defaults to None.
    +        mode (str, optional): how the content will be encoded, automatic if not specified. Defaults to None.
    +        encoding (str, optional): Encoding of the specfied content string. Defaults to None.
    +        scale (int, optional): scale of the QR code relative to the module. Defaults to 1.
    +    """
    +    qr_object = _get_qr_object(content, version, mode, encoding)
    +    return qr_object.png_as_base64_str(scale=scale)
    +
    +
    +
    +def png_get(content, file_path, version=None, mode=None, encoding=None, scale=1) +
    +
    +

    Writes QR code to file in PNG format

    +

    Args

    +
    +
    content : str
    +
    content to be encoded
    +
    file_path : str
    +
    file path to write to
    +
    version : int, optional
    +
    data capacity of the code, automatic if not specified. Defaults to None.
    +
    mode : str, optional
    +
    how the content will be encoded, automatic if not specified. Defaults to None.
    +
    encoding : str, optional
    +
    Encoding of the specfied content string. Defaults to None.
    +
    scale : int, optional
    +
    scale of the QR code relative to the module. Defaults to 1.
    +
    +
    + +Expand source code + +
    def png_get(content, file_path, version=None, mode=None, encoding=None, scale=1):
    +    """Writes QR code to file in PNG format
    +
    +    Args:
    +        content (str): content to be encoded
    +        file_path (str): file path to write to
    +        version (int, optional): data capacity of the code, automatic if not specified. Defaults to None.
    +        mode (str, optional): how the content will be encoded, automatic if not specified. Defaults to None.
    +        encoding (str, optional): Encoding of the specfied content string. Defaults to None.
    +        scale (int, optional): scale of the QR code relative to the module. Defaults to 1.
    +    """
    +    qr_object = _get_qr_object(content, version, mode, encoding)
    +    qr_object.png(file_path, scale=scale)
    +
    +
    +
    +def svg_get(content, file_path, version=None, mode=None, encoding=None, scale=1, title=None) +
    +
    +

    Writes QR code to file in SVG format

    +

    Args

    +
    +
    content : str
    +
    content to be encoded
    +
    file_path : str
    +
    file path to write to
    +
    version : int, optional
    +
    data capacity of the code, automatic if not specified. Defaults to None.
    +
    mode : str, optional
    +
    how the content will be encoded, automatic if not specified. Defaults to None.
    +
    encoding : str, optional
    +
    Encoding of the specfied content string. Defaults to None.
    +
    scale : int, optional
    +
    scale of the QR code relative to the module. Defaults to 1.
    +
    title : str, optional
    +
    title of the SVG. Defauls to None.
    +
    +
    + +Expand source code + +
    def svg_get(content, file_path, version=None, mode=None, encoding=None, scale=1, title=None):
    +    """Writes QR code to file in SVG format
    +
    +    Args:
    +        content (str): content to be encoded
    +        file_path (str): file path to write to
    +        version (int, optional): data capacity of the code, automatic if not specified. Defaults to None.
    +        mode (str, optional): how the content will be encoded, automatic if not specified. Defaults to None.
    +        encoding (str, optional): Encoding of the specfied content string. Defaults to None.
    +        scale (int, optional): scale of the QR code relative to the module. Defaults to 1.
    +        title (str, optional): title of the SVG. Defauls to None.
    +    """
    +    qr_object = _get_qr_object(content, version, mode, encoding)
    +    qr_object.svg(file_path, scale=scale, title=title)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/redis/index.html b/docs/api/jumpscale/tools/redis/index.html new file mode 100644 index 000000000..4aae1760b --- /dev/null +++ b/docs/api/jumpscale/tools/redis/index.html @@ -0,0 +1,99 @@ + + + + + + +jumpscale.tools.redis API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.redis

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .redis import RedisServer
    +
    +    return StoredFactory(RedisServer)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.tools.redis.redis
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .redis import RedisServer
    +
    +    return StoredFactory(RedisServer)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/redis/redis.html b/docs/api/jumpscale/tools/redis/redis.html new file mode 100644 index 000000000..5c2e1a4fc --- /dev/null +++ b/docs/api/jumpscale/tools/redis/redis.html @@ -0,0 +1,354 @@ + + + + + + +jumpscale.tools.redis.redis API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.redis.redis

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +from jumpscale.core.base import Base, fields
    +import shutil
    +
    +
    +class RedisServer(Base):
    +
    +    host = fields.String(default="127.0.0.1")
    +    port = fields.Integer(default=6379)
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._cmd = j.tools.startupcmd.get(f"redis_{self.instance_name}")
    +
    +    @property
    +    def cmd(self):
    +        self._cmd.start_cmd = f"redis-server --bind {self.host} --port {self.port}"
    +        self._cmd.ports = [self.port]
    +        return self._cmd
    +
    +    @property
    +    def installed(self) -> bool:
    +        """check if redis server is installed
    +
    +        Returns:
    +            bool: True if redis server is installed
    +        """
    +        return shutil.which("redis-server")
    +
    +    def start(self):
    +        """start redis server in tmux
    +        """
    +        # Port is not busy (Redis is not started)
    +        if not j.sals.nettools.tcp_connection_test(self.host, self.port, timeout=1):
    +            self.cmd.start()
    +
    +    def stop(self):
    +        """stop redis server
    +        """
    +        self.cmd.stop(force=True)
    +
    +    def restart(self):
    +        """restart redis server
    +        """
    +        self.stop()
    +        self.start()
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class RedisServer +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class RedisServer(Base):
    +
    +    host = fields.String(default="127.0.0.1")
    +    port = fields.Integer(default=6379)
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self._cmd = j.tools.startupcmd.get(f"redis_{self.instance_name}")
    +
    +    @property
    +    def cmd(self):
    +        self._cmd.start_cmd = f"redis-server --bind {self.host} --port {self.port}"
    +        self._cmd.ports = [self.port]
    +        return self._cmd
    +
    +    @property
    +    def installed(self) -> bool:
    +        """check if redis server is installed
    +
    +        Returns:
    +            bool: True if redis server is installed
    +        """
    +        return shutil.which("redis-server")
    +
    +    def start(self):
    +        """start redis server in tmux
    +        """
    +        # Port is not busy (Redis is not started)
    +        if not j.sals.nettools.tcp_connection_test(self.host, self.port, timeout=1):
    +            self.cmd.start()
    +
    +    def stop(self):
    +        """stop redis server
    +        """
    +        self.cmd.stop(force=True)
    +
    +    def restart(self):
    +        """restart redis server
    +        """
    +        self.stop()
    +        self.start()
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var cmd
    +
    +
    +
    + +Expand source code + +
    @property
    +def cmd(self):
    +    self._cmd.start_cmd = f"redis-server --bind {self.host} --port {self.port}"
    +    self._cmd.ports = [self.port]
    +    return self._cmd
    +
    +
    +
    var host
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var installed : bool
    +
    +

    check if redis server is installed

    +

    Returns

    +
    +
    bool
    +
    True if redis server is installed
    +
    +
    + +Expand source code + +
    @property
    +def installed(self) -> bool:
    +    """check if redis server is installed
    +
    +    Returns:
    +        bool: True if redis server is installed
    +    """
    +    return shutil.which("redis-server")
    +
    +
    +
    var port
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def restart(self) +
    +
    +

    restart redis server

    +
    + +Expand source code + +
    def restart(self):
    +    """restart redis server
    +    """
    +    self.stop()
    +    self.start()
    +
    +
    +
    +def start(self) +
    +
    +

    start redis server in tmux

    +
    + +Expand source code + +
    def start(self):
    +    """start redis server in tmux
    +    """
    +    # Port is not busy (Redis is not started)
    +    if not j.sals.nettools.tcp_connection_test(self.host, self.port, timeout=1):
    +        self.cmd.start()
    +
    +
    +
    +def stop(self) +
    +
    +

    stop redis server

    +
    + +Expand source code + +
    def stop(self):
    +    """stop redis server
    +    """
    +    self.cmd.stop(force=True)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/restic/index.html b/docs/api/jumpscale/tools/restic/index.html new file mode 100644 index 000000000..8844b10b8 --- /dev/null +++ b/docs/api/jumpscale/tools/restic/index.html @@ -0,0 +1,99 @@ + + + + + + +jumpscale.tools.restic API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.restic

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .restic import ResticRepo
    +
    +    return StoredFactory(ResticRepo)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.tools.restic.restic
    +
    +

    Restic client …

    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .restic import ResticRepo
    +
    +    return StoredFactory(ResticRepo)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/restic/restic.html b/docs/api/jumpscale/tools/restic/restic.html new file mode 100644 index 000000000..2381f2148 --- /dev/null +++ b/docs/api/jumpscale/tools/restic/restic.html @@ -0,0 +1,1249 @@ + + + + + + +jumpscale.tools.restic.restic API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.restic.restic

    +
    +
    +

    Restic client

    +

    Tool to create restic repo using different backends.

    +

    prerequisites

    +

    Make sure to isntall restic(https://restic.readthedocs.io/en/latest/020_installation.html) +On linux it can be installed as follows:

    +
    apt-get install restic
    +
    +

    Usage

    +

    Basic usage

    +
    instance = j.clients.restic.get("instance")
    +instance.repo = /path/to/repo  # Where restic will store its data can be local or SFTP or rest check https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html
    +instance.password = pass  # Password for the repo needed to access the data after init
    +instance.init_repo()
    +
    +

    Other Backends

    +

    To use backend such as minio you will need to set extra environment for accessing the servers.

    +
    instance.extra_env = {'AWS_ACCESS_KEY_ID': 'KEY',
    +                      'AWS_SECRET_ACCESS_KEY': 'SECRET'}
    +
    +

    Backing up

    +
    instance.backup("/my/path")
    +
    +

    Listing snapshots

    +
    instance.list_snapshots()
    +[{'time': '2020-07-15T13:44:05.767265958Z',
    +  'tree': '93254cf97720d264d362c9b8d91889643e45886fe0d6b80027d2cef3910bd43d',
    +  'paths': ['/tmp/stc'],
    +  'hostname': 'jsng',
    +  'username': 'root',
    +  'id': 'e3330cf26ff4ada6c80868c08626f38fa2fc0185b0510c1bb7c5fa880bc67d0c',
    +  'short_id': 'e3330cf2'}]
    +
    +

    Restoring

    +
    instance.restore("/path/to/store", "e3330cf26ff4ada6c80868c08626f38fa2fc0185b0510c1bb7c5fa880bc67d0c")  # Will restore specified snapshot to path
    +instance.restore("/path/to/store", latest=True, path="/tmp/stc", host="jsng")  # Will return latest snapshot with the specified filters
    +
    +

    Pruning data

    +
    instance.forget(keep_last=10)  # Will prune all snapshots and keep the last 10 taken only
    +
    +
    + +Expand source code + +
    """
    +# Restic client
    +
    +Tool to create restic repo using different backends.
    +
    +## prerequisites
    +Make sure to isntall restic(https://restic.readthedocs.io/en/latest/020_installation.html)
    +On linux it can be installed as follows:
    +
    +```bash
    +apt-get install restic
    +```
    +
    +## Usage
    +
    +### Basic usage
    +
    +```python
    +instance = j.clients.restic.get("instance")
    +instance.repo = /path/to/repo  # Where restic will store its data can be local or SFTP or rest check https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html
    +instance.password = pass  # Password for the repo needed to access the data after init
    +instance.init_repo()
    +```
    +
    +### Other Backends
    +
    +To use backend such as minio you will need to set extra environment for accessing the servers.
    +
    +```python
    +instance.extra_env = {'AWS_ACCESS_KEY_ID': 'KEY',
    +                      'AWS_SECRET_ACCESS_KEY': 'SECRET'}
    +```
    +
    +### Backing up
    +
    +```python
    +instance.backup("/my/path")
    +```
    +
    +### Listing snapshots
    +
    +```python
    +instance.list_snapshots()
    +[{'time': '2020-07-15T13:44:05.767265958Z',
    +  'tree': '93254cf97720d264d362c9b8d91889643e45886fe0d6b80027d2cef3910bd43d',
    +  'paths': ['/tmp/stc'],
    +  'hostname': 'jsng',
    +  'username': 'root',
    +  'id': 'e3330cf26ff4ada6c80868c08626f38fa2fc0185b0510c1bb7c5fa880bc67d0c',
    +  'short_id': 'e3330cf2'}]
    +```
    +
    +### Restoring
    +
    +```python
    +instance.restore("/path/to/store", "e3330cf26ff4ada6c80868c08626f38fa2fc0185b0510c1bb7c5fa880bc67d0c")  # Will restore specified snapshot to path
    +instance.restore("/path/to/store", latest=True, path="/tmp/stc", host="jsng")  # Will return latest snapshot with the specified filters
    +```
    +
    +### Pruning data
    +
    +```python
    +instance.forget(keep_last=10)  # Will prune all snapshots and keep the last 10 taken only
    +```
    +"""
    +from jumpscale.core.base import Base, fields
    +from jumpscale.core.exceptions import NotFound, Runtime
    +from io import BytesIO
    +
    +import subprocess
    +import json
    +import os
    +
    +
    +CRON_SCRIPT = """
    +epoch=$(date +%s)
    +
    +export RESTIC_REPOSITORY={repo}
    +export RESTIC_PASSWORD={password}
    +
    +restic unlock &
    +wait $!
    +
    +restic backup --one-file-system --tag $epoch {path} &
    +wait $!
    +
    +restic forget --keep-last {keep_last} --prune &
    +wait $!
    +
    +restic check &
    +wait $!
    +"""
    +
    +WATCHDOG_SCRIPT = """
    +epoch=$(date +%s)
    +
    +export RESTIC_REPOSITORY={repo}
    +export RESTIC_PASSWORD={password}
    +export AWS_ACCESS_KEY_ID={AWS_ACCESS_KEY_ID}
    +export AWS_SECRET_ACCESS_KEY={AWS_SECRET_ACCESS_KEY}
    +
    +
    +latest_backup=`restic snapshots --last -c -q | sed -n 3p |cut -d " " -f3`
    +latest_backup_epoch=`date "+%s" -d "$latest_backup"`
    +wait $!
    +
    +current_date=`date +"%s"`
    +
    +if [[ $(( (current_date - latest_backup_epoch) / 86400 )) > 2 ]]
    +then
    +    echo "No backups is being taken since 2 days, sending escalation mail..."
    +    jsng 'j.clients.mail.escalation_instance.send("{ESCALATION_MAIL}", subject="{THREEBOT_NAME} 3Bot backups warning", message="We noticed that there were no backups taken since 2 days for {THREEBOT_NAME} 3Bot. Please check the support")'
    +fi
    +
    +"""
    +
    +
    +class ResticRepo(Base):
    +
    +    repo = fields.String(required=True)
    +    password = fields.Secret(required=True)
    +    extra_env = fields.Typed(dict, default={})
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +        self._check_install("restic")
    +        self._env = None
    +
    +    def _check_install(self, binary):
    +        if subprocess.call(["which", binary], stdout=subprocess.DEVNULL):
    +            raise NotFound(f"{binary} not installed")
    +
    +    @property
    +    def env(self):
    +        self.validate()
    +        self._env = os.environ.copy()
    +        self._env.update({"RESTIC_PASSWORD": self.password, "RESTIC_REPOSITORY": self.repo}, **self.extra_env)
    +        return self._env
    +
    +    def _run_cmd(self, cmd, check=True):
    +        proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.env)
    +        if check and proc.returncode:
    +            raise Runtime(f"Restic command failed with {proc.stderr.decode()}")
    +        return proc
    +
    +    def init_repo(self):
    +        """Init restic repo with data specified in the instances
    +        """
    +        proc = self._run_cmd(["restic", "cat", "config"], False)
    +        if proc.returncode > 0:
    +            self._run_cmd(["restic", "init"])
    +
    +    def backup(self, path, tags=None, exclude=None):
    +        """Backup a path to the repo.
    +
    +        Args:
    +            path (str or list of str): Local path/s to backup.
    +            tags (list): List of tags to set to the backup.
    +            exclude (list of str): This instructs restic to exclude files matching a given pattern/s.
    +        """
    +        if not path:
    +            raise ValueError("Please specify path/s to backup")
    +        cmd = ["restic", "backup"]
    +        tags = tags or []
    +        for tag in tags:
    +            cmd.extend(["--tag", tag])
    +        if exclude:
    +            for pattern in exclude:
    +                cmd.extend([f"--exclude={pattern}"])
    +        if isinstance(path, list):
    +            cmd.extend(path)
    +        else:
    +            cmd.extend([path])
    +        self._run_cmd(cmd)
    +
    +    def restore(self, target_path, snapshot_id=None, latest=True, path=None, host=None, tags=None):
    +        """Restores a snapshot.
    +
    +        Args:
    +            target_path (str): a path to restore to
    +            snapshot_id (str, optional): Id of the snapshot. Defaults to None.
    +            latest (bool, optional): if True will use latest snapshot. Defaults to True.
    +            path (str, optional): Filter on the path when using latest. Defaults to None.
    +            host (str, optional): Filter on the hostname when using latest. Defaults to None.
    +            tags (list, optional): List of tags to filter on.
    +        """
    +        cmd = ["restic", "--target", target_path, "restore"]
    +        if snapshot_id:
    +            cmd.append(snapshot_id)
    +            self._run_cmd(cmd)
    +        elif latest:
    +            args = ["latest"]
    +            if path:
    +                args.extend(["--path", path])
    +            if host:
    +                args.extend(["--host", host])
    +            if tags:
    +                for tag in tags:
    +                    cmd.extend(["--tag", tag])
    +            self._run_cmd(cmd + args)
    +        else:
    +            raise ValueError("Please specify either `snapshot_id` or `latest` flag")
    +
    +    def list_snapshots(self, tags=None, last=False, path=None):
    +        """List all snapshots in the repo.
    +
    +        Args:
    +            tags (list, optional): a list of tags to filter on. Defaults to None.
    +            last (bool): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +            path (str): a path to filter on. Defaults to None.
    +
    +        Returns
    +            list : all snapshots as dicts
    +        """
    +        tags = tags or []
    +        cmd = ["restic", "snapshots", "--json"]
    +        for tag in tags:
    +            cmd.extend(["--tag", tag])
    +
    +        if path:
    +            cmd.extend(["--path", path])
    +        if last:
    +            cmd.append("--last")
    +        proc = self._run_cmd(cmd)
    +        return json.loads(proc.stdout)
    +
    +    def forget(self, keep_last=10, prune=True, snapshots=None, tags=None):
    +        """Remove snapshots and Optionally remove the data that was referenced by those snapshots.
    +        During a prune operation, the repository is locked and backups cannot be completed.
    +
    +        Args:
    +            keep_last (str, optional): How many items to keep. Defaults to 10.
    +            prune (bool, optional): Whether to actually remove the data or not. Defaults to True.
    +            snapshots (list, optional): a list of specifics snapshot ids to forget. if given, the value of keep_last parm will be ignored. Defaults to None.
    +            tags (list, optional): if given, Only the snapshots which have specified tags are considered. Defaults to None.
    +        """
    +        cmd = ["restic", "forget"]
    +        if tags:
    +            for tag in tags:
    +                cmd.extend(["--tag", tag])
    +        if keep_last and not snapshots:  # will be ignored in case if passing snapshot id/s. this is a restic behaviour.
    +            cmd.extend(["--keep-last", str(keep_last)])
    +        if prune:
    +            cmd.append("--prune")
    +        if snapshots:
    +            cmd.extend(snapshots)
    +        self._run_cmd(cmd)
    +
    +    def _get_script_path(self, path):
    +        return os.path.join(path, f"{self.instance_name}_restic_cron")
    +
    +    def _get_crons_jobs(self):
    +        proc = subprocess.run(["crontab", "-l"], stderr=subprocess.DEVNULL, stdout=subprocess.PIPE)
    +        return proc.stdout.decode()
    +
    +    def auto_backup(self, path, keep_last=20):
    +        """Runs a cron job that backups the repo and prunes the last specified backups.
    +
    +        Args:
    +            path (str): Local path to backup.
    +            keep_last (int, optional): How many items to keep in every forgot operation. Defaults to 20.
    +        """
    +        self._check_install("crontab")
    +        script_path = self._get_script_path(path)
    +        cronjobs = self._get_crons_jobs()
    +        if not self.auto_backup_running(path):  # Check if cron job already running
    +            cron_script = CRON_SCRIPT.format(repo=self.repo, password=self.password, path=path, keep_last=keep_last)
    +            with open(script_path, "w") as rfd:
    +                rfd.write(cron_script)
    +
    +            cron_cmd = cronjobs + f"0 0 * * * bash {script_path} \n"
    +            proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +            proc_res = proc.communicate(input=cron_cmd.encode())
    +            if proc.returncode > 0:
    +                raise Runtime(f"Couldn't start cron job, failed with {proc_res[1]}")
    +
    +    def auto_backup_running(self, path):
    +        """Checks if auto backup for the specified path is running or not
    +
    +        Args:
    +            path (str): Local path to backup in the cron job.
    +
    +        Returns:
    +            bool: Whether it is running or not.
    +        """
    +        script_path = self._get_script_path(path)
    +        cronjobs = self._get_crons_jobs()
    +        return cronjobs.find(script_path) >= 0
    +
    +    def disable_auto_backup(self, path):
    +        """Removes cron jon based on the path being backed.
    +
    +        Args:
    +            path (str): Local path to backup in the cron job.
    +        """
    +        script_path = self._get_script_path(path)
    +        cronjobs = self._get_crons_jobs()
    +        other_crons = []
    +        for cronjob in cronjobs.splitlines():
    +            if script_path not in cronjob:
    +                other_crons.append(cronjob)
    +        proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +        cron_cmd = "\n".join(other_crons) + "\n"
    +        proc_res = proc.communicate(input=cron_cmd.encode())
    +        if proc.returncode > 0:
    +            raise Runtime(f"Couldn't remove cron job, failed with {proc_res[1]}")
    +
    +    def backup_watchdog_running(self, script_path) -> bool:
    +        """Watches a cronjob to watch backups using last snapshot time.
    +
    +        Args:
    +            script_path (str): a path to the script to run the cronjob.
    +        Returns:
    +            bool: True if the backup watchdog running otherwise False
    +        """
    +        script_path = self._get_script_path(script_path)
    +        cronjobs = self._get_crons_jobs()
    +        return cronjobs.find(script_path) >= 0
    +
    +    def start_watch_backup(self, path):
    +        """Runs a cron job that backups the repo and prunes the last specified backups.
    +
    +        Args:
    +            path (str): Local path to backup.
    +            keep_last (int, optional): How many items to keep in every forgot operation. Defaults to 20.
    +        """
    +        self._check_install("crontab")
    +        script_path = self._get_script_path(path)
    +        cronjobs = self._get_crons_jobs()
    +        if not self.backup_watchdog_running(path):  # Check if cron job already running
    +            cron_script = WATCHDOG_SCRIPT.format(
    +                repo=self.repo,
    +                password=self.password,
    +                path=path,
    +                AWS_SECRET_ACCESS_KEY=self.extra_env.get("AWS_SECRET_ACCESS_KEY"),
    +                AWS_ACCESS_KEY_ID=self.extra_env.get("AWS_ACCESS_KEY_ID"),
    +                THREEBOT_NAME=os.environ.get("THREEBOT_NAME"),
    +                ESCALATION_MAIL=os.environ.get("ESCALATION_MAIL"),
    +            )
    +
    +            with open(script_path, "w") as rfd:
    +                rfd.write(cron_script)
    +
    +            cron_cmd = cronjobs + f"0 0 */2 * * bash {script_path} \n"
    +            proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +            proc_res = proc.communicate(input=cron_cmd.encode())
    +            if proc.returncode > 0:
    +                raise Runtime(f"Couldn't start cron job, failed with {proc_res[1]}")
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class ResticRepo +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class ResticRepo(Base):
    +
    +    repo = fields.String(required=True)
    +    password = fields.Secret(required=True)
    +    extra_env = fields.Typed(dict, default={})
    +
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +        self._check_install("restic")
    +        self._env = None
    +
    +    def _check_install(self, binary):
    +        if subprocess.call(["which", binary], stdout=subprocess.DEVNULL):
    +            raise NotFound(f"{binary} not installed")
    +
    +    @property
    +    def env(self):
    +        self.validate()
    +        self._env = os.environ.copy()
    +        self._env.update({"RESTIC_PASSWORD": self.password, "RESTIC_REPOSITORY": self.repo}, **self.extra_env)
    +        return self._env
    +
    +    def _run_cmd(self, cmd, check=True):
    +        proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.env)
    +        if check and proc.returncode:
    +            raise Runtime(f"Restic command failed with {proc.stderr.decode()}")
    +        return proc
    +
    +    def init_repo(self):
    +        """Init restic repo with data specified in the instances
    +        """
    +        proc = self._run_cmd(["restic", "cat", "config"], False)
    +        if proc.returncode > 0:
    +            self._run_cmd(["restic", "init"])
    +
    +    def backup(self, path, tags=None, exclude=None):
    +        """Backup a path to the repo.
    +
    +        Args:
    +            path (str or list of str): Local path/s to backup.
    +            tags (list): List of tags to set to the backup.
    +            exclude (list of str): This instructs restic to exclude files matching a given pattern/s.
    +        """
    +        if not path:
    +            raise ValueError("Please specify path/s to backup")
    +        cmd = ["restic", "backup"]
    +        tags = tags or []
    +        for tag in tags:
    +            cmd.extend(["--tag", tag])
    +        if exclude:
    +            for pattern in exclude:
    +                cmd.extend([f"--exclude={pattern}"])
    +        if isinstance(path, list):
    +            cmd.extend(path)
    +        else:
    +            cmd.extend([path])
    +        self._run_cmd(cmd)
    +
    +    def restore(self, target_path, snapshot_id=None, latest=True, path=None, host=None, tags=None):
    +        """Restores a snapshot.
    +
    +        Args:
    +            target_path (str): a path to restore to
    +            snapshot_id (str, optional): Id of the snapshot. Defaults to None.
    +            latest (bool, optional): if True will use latest snapshot. Defaults to True.
    +            path (str, optional): Filter on the path when using latest. Defaults to None.
    +            host (str, optional): Filter on the hostname when using latest. Defaults to None.
    +            tags (list, optional): List of tags to filter on.
    +        """
    +        cmd = ["restic", "--target", target_path, "restore"]
    +        if snapshot_id:
    +            cmd.append(snapshot_id)
    +            self._run_cmd(cmd)
    +        elif latest:
    +            args = ["latest"]
    +            if path:
    +                args.extend(["--path", path])
    +            if host:
    +                args.extend(["--host", host])
    +            if tags:
    +                for tag in tags:
    +                    cmd.extend(["--tag", tag])
    +            self._run_cmd(cmd + args)
    +        else:
    +            raise ValueError("Please specify either `snapshot_id` or `latest` flag")
    +
    +    def list_snapshots(self, tags=None, last=False, path=None):
    +        """List all snapshots in the repo.
    +
    +        Args:
    +            tags (list, optional): a list of tags to filter on. Defaults to None.
    +            last (bool): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +            path (str): a path to filter on. Defaults to None.
    +
    +        Returns
    +            list : all snapshots as dicts
    +        """
    +        tags = tags or []
    +        cmd = ["restic", "snapshots", "--json"]
    +        for tag in tags:
    +            cmd.extend(["--tag", tag])
    +
    +        if path:
    +            cmd.extend(["--path", path])
    +        if last:
    +            cmd.append("--last")
    +        proc = self._run_cmd(cmd)
    +        return json.loads(proc.stdout)
    +
    +    def forget(self, keep_last=10, prune=True, snapshots=None, tags=None):
    +        """Remove snapshots and Optionally remove the data that was referenced by those snapshots.
    +        During a prune operation, the repository is locked and backups cannot be completed.
    +
    +        Args:
    +            keep_last (str, optional): How many items to keep. Defaults to 10.
    +            prune (bool, optional): Whether to actually remove the data or not. Defaults to True.
    +            snapshots (list, optional): a list of specifics snapshot ids to forget. if given, the value of keep_last parm will be ignored. Defaults to None.
    +            tags (list, optional): if given, Only the snapshots which have specified tags are considered. Defaults to None.
    +        """
    +        cmd = ["restic", "forget"]
    +        if tags:
    +            for tag in tags:
    +                cmd.extend(["--tag", tag])
    +        if keep_last and not snapshots:  # will be ignored in case if passing snapshot id/s. this is a restic behaviour.
    +            cmd.extend(["--keep-last", str(keep_last)])
    +        if prune:
    +            cmd.append("--prune")
    +        if snapshots:
    +            cmd.extend(snapshots)
    +        self._run_cmd(cmd)
    +
    +    def _get_script_path(self, path):
    +        return os.path.join(path, f"{self.instance_name}_restic_cron")
    +
    +    def _get_crons_jobs(self):
    +        proc = subprocess.run(["crontab", "-l"], stderr=subprocess.DEVNULL, stdout=subprocess.PIPE)
    +        return proc.stdout.decode()
    +
    +    def auto_backup(self, path, keep_last=20):
    +        """Runs a cron job that backups the repo and prunes the last specified backups.
    +
    +        Args:
    +            path (str): Local path to backup.
    +            keep_last (int, optional): How many items to keep in every forgot operation. Defaults to 20.
    +        """
    +        self._check_install("crontab")
    +        script_path = self._get_script_path(path)
    +        cronjobs = self._get_crons_jobs()
    +        if not self.auto_backup_running(path):  # Check if cron job already running
    +            cron_script = CRON_SCRIPT.format(repo=self.repo, password=self.password, path=path, keep_last=keep_last)
    +            with open(script_path, "w") as rfd:
    +                rfd.write(cron_script)
    +
    +            cron_cmd = cronjobs + f"0 0 * * * bash {script_path} \n"
    +            proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +            proc_res = proc.communicate(input=cron_cmd.encode())
    +            if proc.returncode > 0:
    +                raise Runtime(f"Couldn't start cron job, failed with {proc_res[1]}")
    +
    +    def auto_backup_running(self, path):
    +        """Checks if auto backup for the specified path is running or not
    +
    +        Args:
    +            path (str): Local path to backup in the cron job.
    +
    +        Returns:
    +            bool: Whether it is running or not.
    +        """
    +        script_path = self._get_script_path(path)
    +        cronjobs = self._get_crons_jobs()
    +        return cronjobs.find(script_path) >= 0
    +
    +    def disable_auto_backup(self, path):
    +        """Removes cron jon based on the path being backed.
    +
    +        Args:
    +            path (str): Local path to backup in the cron job.
    +        """
    +        script_path = self._get_script_path(path)
    +        cronjobs = self._get_crons_jobs()
    +        other_crons = []
    +        for cronjob in cronjobs.splitlines():
    +            if script_path not in cronjob:
    +                other_crons.append(cronjob)
    +        proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +        cron_cmd = "\n".join(other_crons) + "\n"
    +        proc_res = proc.communicate(input=cron_cmd.encode())
    +        if proc.returncode > 0:
    +            raise Runtime(f"Couldn't remove cron job, failed with {proc_res[1]}")
    +
    +    def backup_watchdog_running(self, script_path) -> bool:
    +        """Watches a cronjob to watch backups using last snapshot time.
    +
    +        Args:
    +            script_path (str): a path to the script to run the cronjob.
    +        Returns:
    +            bool: True if the backup watchdog running otherwise False
    +        """
    +        script_path = self._get_script_path(script_path)
    +        cronjobs = self._get_crons_jobs()
    +        return cronjobs.find(script_path) >= 0
    +
    +    def start_watch_backup(self, path):
    +        """Runs a cron job that backups the repo and prunes the last specified backups.
    +
    +        Args:
    +            path (str): Local path to backup.
    +            keep_last (int, optional): How many items to keep in every forgot operation. Defaults to 20.
    +        """
    +        self._check_install("crontab")
    +        script_path = self._get_script_path(path)
    +        cronjobs = self._get_crons_jobs()
    +        if not self.backup_watchdog_running(path):  # Check if cron job already running
    +            cron_script = WATCHDOG_SCRIPT.format(
    +                repo=self.repo,
    +                password=self.password,
    +                path=path,
    +                AWS_SECRET_ACCESS_KEY=self.extra_env.get("AWS_SECRET_ACCESS_KEY"),
    +                AWS_ACCESS_KEY_ID=self.extra_env.get("AWS_ACCESS_KEY_ID"),
    +                THREEBOT_NAME=os.environ.get("THREEBOT_NAME"),
    +                ESCALATION_MAIL=os.environ.get("ESCALATION_MAIL"),
    +            )
    +
    +            with open(script_path, "w") as rfd:
    +                rfd.write(cron_script)
    +
    +            cron_cmd = cronjobs + f"0 0 */2 * * bash {script_path} \n"
    +            proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +            proc_res = proc.communicate(input=cron_cmd.encode())
    +            if proc.returncode > 0:
    +                raise Runtime(f"Couldn't start cron job, failed with {proc_res[1]}")
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Instance variables

    +
    +
    var env
    +
    +
    +
    + +Expand source code + +
    @property
    +def env(self):
    +    self.validate()
    +    self._env = os.environ.copy()
    +    self._env.update({"RESTIC_PASSWORD": self.password, "RESTIC_REPOSITORY": self.repo}, **self.extra_env)
    +    return self._env
    +
    +
    +
    var extra_env
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var password
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    var repo
    +
    +

    getter method this property

    +

    will call _get_value, which would if the value is already defined +and will get the default value if not

    +

    Returns

    +
    +
    any
    +
    the field value
    +
    +
    + +Expand source code + +
    def getter(self):
    +    """
    +    getter method this property
    +
    +    will call `_get_value`, which would if the value is already defined
    +    and will get the default value if not
    +
    +    Returns:
    +        any: the field value
    +    """
    +    return self._get_value(name, field)
    +
    +
    +
    +

    Methods

    +
    +
    +def auto_backup(self, path, keep_last=20) +
    +
    +

    Runs a cron job that backups the repo and prunes the last specified backups.

    +

    Args

    +
    +
    path : str
    +
    Local path to backup.
    +
    keep_last : int, optional
    +
    How many items to keep in every forgot operation. Defaults to 20.
    +
    +
    + +Expand source code + +
    def auto_backup(self, path, keep_last=20):
    +    """Runs a cron job that backups the repo and prunes the last specified backups.
    +
    +    Args:
    +        path (str): Local path to backup.
    +        keep_last (int, optional): How many items to keep in every forgot operation. Defaults to 20.
    +    """
    +    self._check_install("crontab")
    +    script_path = self._get_script_path(path)
    +    cronjobs = self._get_crons_jobs()
    +    if not self.auto_backup_running(path):  # Check if cron job already running
    +        cron_script = CRON_SCRIPT.format(repo=self.repo, password=self.password, path=path, keep_last=keep_last)
    +        with open(script_path, "w") as rfd:
    +            rfd.write(cron_script)
    +
    +        cron_cmd = cronjobs + f"0 0 * * * bash {script_path} \n"
    +        proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +        proc_res = proc.communicate(input=cron_cmd.encode())
    +        if proc.returncode > 0:
    +            raise Runtime(f"Couldn't start cron job, failed with {proc_res[1]}")
    +
    +
    +
    +def auto_backup_running(self, path) +
    +
    +

    Checks if auto backup for the specified path is running or not

    +

    Args

    +
    +
    path : str
    +
    Local path to backup in the cron job.
    +
    +

    Returns

    +
    +
    bool
    +
    Whether it is running or not.
    +
    +
    + +Expand source code + +
    def auto_backup_running(self, path):
    +    """Checks if auto backup for the specified path is running or not
    +
    +    Args:
    +        path (str): Local path to backup in the cron job.
    +
    +    Returns:
    +        bool: Whether it is running or not.
    +    """
    +    script_path = self._get_script_path(path)
    +    cronjobs = self._get_crons_jobs()
    +    return cronjobs.find(script_path) >= 0
    +
    +
    +
    +def backup(self, path, tags=None, exclude=None) +
    +
    +

    Backup a path to the repo.

    +

    Args

    +
    +
    path : str or list of str
    +
    Local path/s to backup.
    +
    tags : list
    +
    List of tags to set to the backup.
    +
    exclude : list of str
    +
    This instructs restic to exclude files matching a given pattern/s.
    +
    +
    + +Expand source code + +
    def backup(self, path, tags=None, exclude=None):
    +    """Backup a path to the repo.
    +
    +    Args:
    +        path (str or list of str): Local path/s to backup.
    +        tags (list): List of tags to set to the backup.
    +        exclude (list of str): This instructs restic to exclude files matching a given pattern/s.
    +    """
    +    if not path:
    +        raise ValueError("Please specify path/s to backup")
    +    cmd = ["restic", "backup"]
    +    tags = tags or []
    +    for tag in tags:
    +        cmd.extend(["--tag", tag])
    +    if exclude:
    +        for pattern in exclude:
    +            cmd.extend([f"--exclude={pattern}"])
    +    if isinstance(path, list):
    +        cmd.extend(path)
    +    else:
    +        cmd.extend([path])
    +    self._run_cmd(cmd)
    +
    +
    +
    +def backup_watchdog_running(self, script_path) ‑> bool +
    +
    +

    Watches a cronjob to watch backups using last snapshot time.

    +

    Args

    +
    +
    script_path : str
    +
    a path to the script to run the cronjob.
    +
    +

    Returns

    +
    +
    bool
    +
    True if the backup watchdog running otherwise False
    +
    +
    + +Expand source code + +
    def backup_watchdog_running(self, script_path) -> bool:
    +    """Watches a cronjob to watch backups using last snapshot time.
    +
    +    Args:
    +        script_path (str): a path to the script to run the cronjob.
    +    Returns:
    +        bool: True if the backup watchdog running otherwise False
    +    """
    +    script_path = self._get_script_path(script_path)
    +    cronjobs = self._get_crons_jobs()
    +    return cronjobs.find(script_path) >= 0
    +
    +
    +
    +def disable_auto_backup(self, path) +
    +
    +

    Removes cron jon based on the path being backed.

    +

    Args

    +
    +
    path : str
    +
    Local path to backup in the cron job.
    +
    +
    + +Expand source code + +
    def disable_auto_backup(self, path):
    +    """Removes cron jon based on the path being backed.
    +
    +    Args:
    +        path (str): Local path to backup in the cron job.
    +    """
    +    script_path = self._get_script_path(path)
    +    cronjobs = self._get_crons_jobs()
    +    other_crons = []
    +    for cronjob in cronjobs.splitlines():
    +        if script_path not in cronjob:
    +            other_crons.append(cronjob)
    +    proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +    cron_cmd = "\n".join(other_crons) + "\n"
    +    proc_res = proc.communicate(input=cron_cmd.encode())
    +    if proc.returncode > 0:
    +        raise Runtime(f"Couldn't remove cron job, failed with {proc_res[1]}")
    +
    +
    +
    +def forget(self, keep_last=10, prune=True, snapshots=None, tags=None) +
    +
    +

    Remove snapshots and Optionally remove the data that was referenced by those snapshots. +During a prune operation, the repository is locked and backups cannot be completed.

    +

    Args

    +
    +
    keep_last : str, optional
    +
    How many items to keep. Defaults to 10.
    +
    prune : bool, optional
    +
    Whether to actually remove the data or not. Defaults to True.
    +
    snapshots : list, optional
    +
    a list of specifics snapshot ids to forget. if given, the value of keep_last parm will be ignored. Defaults to None.
    +
    tags : list, optional
    +
    if given, Only the snapshots which have specified tags are considered. Defaults to None.
    +
    +
    + +Expand source code + +
    def forget(self, keep_last=10, prune=True, snapshots=None, tags=None):
    +    """Remove snapshots and Optionally remove the data that was referenced by those snapshots.
    +    During a prune operation, the repository is locked and backups cannot be completed.
    +
    +    Args:
    +        keep_last (str, optional): How many items to keep. Defaults to 10.
    +        prune (bool, optional): Whether to actually remove the data or not. Defaults to True.
    +        snapshots (list, optional): a list of specifics snapshot ids to forget. if given, the value of keep_last parm will be ignored. Defaults to None.
    +        tags (list, optional): if given, Only the snapshots which have specified tags are considered. Defaults to None.
    +    """
    +    cmd = ["restic", "forget"]
    +    if tags:
    +        for tag in tags:
    +            cmd.extend(["--tag", tag])
    +    if keep_last and not snapshots:  # will be ignored in case if passing snapshot id/s. this is a restic behaviour.
    +        cmd.extend(["--keep-last", str(keep_last)])
    +    if prune:
    +        cmd.append("--prune")
    +    if snapshots:
    +        cmd.extend(snapshots)
    +    self._run_cmd(cmd)
    +
    +
    +
    +def init_repo(self) +
    +
    +

    Init restic repo with data specified in the instances

    +
    + +Expand source code + +
    def init_repo(self):
    +    """Init restic repo with data specified in the instances
    +    """
    +    proc = self._run_cmd(["restic", "cat", "config"], False)
    +    if proc.returncode > 0:
    +        self._run_cmd(["restic", "init"])
    +
    +
    +
    +def list_snapshots(self, tags=None, last=False, path=None) +
    +
    +

    List all snapshots in the repo.

    +

    Args

    +
    +
    tags : list, optional
    +
    a list of tags to filter on. Defaults to None.
    +
    last : bool
    +
    If True will get last snapshot only while respecting the other filters. Defaults to False.
    +
    path : str
    +
    a path to filter on. Defaults to None.
    +
    +

    Returns +list : all snapshots as dicts

    +
    + +Expand source code + +
    def list_snapshots(self, tags=None, last=False, path=None):
    +    """List all snapshots in the repo.
    +
    +    Args:
    +        tags (list, optional): a list of tags to filter on. Defaults to None.
    +        last (bool): If True will get last snapshot only while respecting the other filters. Defaults to False.
    +        path (str): a path to filter on. Defaults to None.
    +
    +    Returns
    +        list : all snapshots as dicts
    +    """
    +    tags = tags or []
    +    cmd = ["restic", "snapshots", "--json"]
    +    for tag in tags:
    +        cmd.extend(["--tag", tag])
    +
    +    if path:
    +        cmd.extend(["--path", path])
    +    if last:
    +        cmd.append("--last")
    +    proc = self._run_cmd(cmd)
    +    return json.loads(proc.stdout)
    +
    +
    +
    +def restore(self, target_path, snapshot_id=None, latest=True, path=None, host=None, tags=None) +
    +
    +

    Restores a snapshot.

    +

    Args

    +
    +
    target_path : str
    +
    a path to restore to
    +
    snapshot_id : str, optional
    +
    Id of the snapshot. Defaults to None.
    +
    latest : bool, optional
    +
    if True will use latest snapshot. Defaults to True.
    +
    path : str, optional
    +
    Filter on the path when using latest. Defaults to None.
    +
    host : str, optional
    +
    Filter on the hostname when using latest. Defaults to None.
    +
    tags : list, optional
    +
    List of tags to filter on.
    +
    +
    + +Expand source code + +
    def restore(self, target_path, snapshot_id=None, latest=True, path=None, host=None, tags=None):
    +    """Restores a snapshot.
    +
    +    Args:
    +        target_path (str): a path to restore to
    +        snapshot_id (str, optional): Id of the snapshot. Defaults to None.
    +        latest (bool, optional): if True will use latest snapshot. Defaults to True.
    +        path (str, optional): Filter on the path when using latest. Defaults to None.
    +        host (str, optional): Filter on the hostname when using latest. Defaults to None.
    +        tags (list, optional): List of tags to filter on.
    +    """
    +    cmd = ["restic", "--target", target_path, "restore"]
    +    if snapshot_id:
    +        cmd.append(snapshot_id)
    +        self._run_cmd(cmd)
    +    elif latest:
    +        args = ["latest"]
    +        if path:
    +            args.extend(["--path", path])
    +        if host:
    +            args.extend(["--host", host])
    +        if tags:
    +            for tag in tags:
    +                cmd.extend(["--tag", tag])
    +        self._run_cmd(cmd + args)
    +    else:
    +        raise ValueError("Please specify either `snapshot_id` or `latest` flag")
    +
    +
    +
    +def start_watch_backup(self, path) +
    +
    +

    Runs a cron job that backups the repo and prunes the last specified backups.

    +

    Args

    +
    +
    path : str
    +
    Local path to backup.
    +
    keep_last : int, optional
    +
    How many items to keep in every forgot operation. Defaults to 20.
    +
    +
    + +Expand source code + +
    def start_watch_backup(self, path):
    +    """Runs a cron job that backups the repo and prunes the last specified backups.
    +
    +    Args:
    +        path (str): Local path to backup.
    +        keep_last (int, optional): How many items to keep in every forgot operation. Defaults to 20.
    +    """
    +    self._check_install("crontab")
    +    script_path = self._get_script_path(path)
    +    cronjobs = self._get_crons_jobs()
    +    if not self.backup_watchdog_running(path):  # Check if cron job already running
    +        cron_script = WATCHDOG_SCRIPT.format(
    +            repo=self.repo,
    +            password=self.password,
    +            path=path,
    +            AWS_SECRET_ACCESS_KEY=self.extra_env.get("AWS_SECRET_ACCESS_KEY"),
    +            AWS_ACCESS_KEY_ID=self.extra_env.get("AWS_ACCESS_KEY_ID"),
    +            THREEBOT_NAME=os.environ.get("THREEBOT_NAME"),
    +            ESCALATION_MAIL=os.environ.get("ESCALATION_MAIL"),
    +        )
    +
    +        with open(script_path, "w") as rfd:
    +            rfd.write(cron_script)
    +
    +        cron_cmd = cronjobs + f"0 0 */2 * * bash {script_path} \n"
    +        proc = subprocess.Popen(["crontab", "-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    +        proc_res = proc.communicate(input=cron_cmd.encode())
    +        if proc.returncode > 0:
    +            raise Runtime(f"Couldn't start cron job, failed with {proc_res[1]}")
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/schemac/compiler.html b/docs/api/jumpscale/tools/schemac/compiler.html index 337c0a7ed..7da3d6914 100644 --- a/docs/api/jumpscale/tools/schemac/compiler.html +++ b/docs/api/jumpscale/tools/schemac/compiler.html @@ -258,4 +258,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/schemac/index.html b/docs/api/jumpscale/tools/schemac/index.html index 8123f226b..15efce43c 100644 --- a/docs/api/jumpscale/tools/schemac/index.html +++ b/docs/api/jumpscale/tools/schemac/index.html @@ -36,7 +36,7 @@

    jsx schema

    now = (T) info = (dict) theemail = (email) -status = "on,off" (E) +status = "on,off" (E) happy = "yes, no" (E) &nr = 4 obj = (O)!hamada.test @@ -127,7 +127,7 @@

    Generated file

    Expand source code
    '''
    -# schemac
    +# schemac 
     
     Schemac is a tool used to convert (transpile) the schemas defined in jsx systems into the new objects definitions in js-ng
     
    @@ -147,7 +147,7 @@ 

    Generated file

    now = (T) info = (dict) theemail = (email) -status = "on,off" (E) +status = "on,off" (E) happy = "yes, no" (E) &nr = 4 obj = (O)!hamada.test @@ -327,4 +327,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/schemac/plugins/crystal.html b/docs/api/jumpscale/tools/schemac/plugins/crystal.html index 42e63cddf..2b123f129 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/crystal.html +++ b/docs/api/jumpscale/tools/schemac/plugins/crystal.html @@ -238,4 +238,4 @@

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/schemac/plugins/index.html b/docs/api/jumpscale/tools/schemac/plugins/index.html index ea69666d9..687079e7e 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/index.html +++ b/docs/api/jumpscale/tools/schemac/plugins/index.html @@ -84,4 +84,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/schemac/plugins/jsng.html b/docs/api/jumpscale/tools/schemac/plugins/jsng.html index bcb66d431..ced79e228 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/jsng.html +++ b/docs/api/jumpscale/tools/schemac/plugins/jsng.html @@ -270,4 +270,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/schemac/plugins/plugin.html b/docs/api/jumpscale/tools/schemac/plugins/plugin.html index ffe958f50..fc76745a2 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/plugin.html +++ b/docs/api/jumpscale/tools/schemac/plugins/plugin.html @@ -244,4 +244,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/schemac/plugins/utils.html b/docs/api/jumpscale/tools/schemac/plugins/utils.html index 5a1008fa5..c4d5f6fad 100644 --- a/docs/api/jumpscale/tools/schemac/plugins/utils.html +++ b/docs/api/jumpscale/tools/schemac/plugins/utils.html @@ -80,4 +80,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/servicemanager/index.html b/docs/api/jumpscale/tools/servicemanager/index.html new file mode 100644 index 000000000..ba11cfa1e --- /dev/null +++ b/docs/api/jumpscale/tools/servicemanager/index.html @@ -0,0 +1,99 @@ + + + + + + +jumpscale.tools.servicemanager API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.servicemanager

    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .servicemanager import ServiceManager
    +
    +    return StoredFactory(ServiceManager)
    +
    +
    +
    +

    Sub-modules

    +
    +
    jumpscale.tools.servicemanager.servicemanager
    +
    +

    Description …

    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def export_module_as() +
    +
    +
    +
    + +Expand source code + +
    def export_module_as():
    +    from jumpscale.core.base import StoredFactory
    +    from .servicemanager import ServiceManager
    +
    +    return StoredFactory(ServiceManager)
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/servicemanager/servicemanager.html b/docs/api/jumpscale/tools/servicemanager/servicemanager.html new file mode 100644 index 000000000..d57bec76c --- /dev/null +++ b/docs/api/jumpscale/tools/servicemanager/servicemanager.html @@ -0,0 +1,809 @@ + + + + + + +jumpscale.tools.servicemanager.servicemanager API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.servicemanager.servicemanager

    +
    +
    +

    Description

    +

    Service manager is the service that monitors and manages background services through gevent greenlets. +Each service defines an interval to define the period of the service and defines also a job method that is run each period. +Any service should: +- Inherit from BackgroundService class defined here: from jumpscale.tools.servicemanager.servicemanager import BackgroundService +- Define an interval in the constructor +- Implement the abstract job method of the BackgroundService base class.

    +

    How it schedules services

    +

    The service manager uses gevent greenlets to run jobs. It spawns the job in a greenlet after its interval period. +Rescheduling the service job is done in by linking a callback to the greenlet which is run after the greenlet finishes. +After the greenlet finishes execution the callback is fired which schedules the job to be run again after another interval.

    +

    To add a service to the service manager you should call the add_service method which takes the package name and package path as parameters. +It loads the file in this path as a module and gets the service object defined in the service.py file.

    +

    Immediately schedule service

    +

    The services first start will be after the requested interval. +If you need to make the service first interval immediately on server start, It can be added in your service by adding

    +
    self.schedule_on_start = True
    +
    +

    in your service init after calling super() init.

    +

    Example service

    +
    from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +
    +class TestService(BackgroundService):
    +    def __init__(self, interval="* * * * *", *args, **kwargs):
    +        '''
    +            Test service that runs every 1 minute
    +        '''
    +        super().__init__(interval, *args, **kwargs)
    +        self.schedule_on_start = True # immediately schedule the service (optional step and the line can be removed, default=False)
    +
    +    def job(self):
    +        print("[Test Service] Done")
    +
    +service = TestService()
    +
    +
    + +Expand source code + +
    """
    +### Description
    +
    +Service manager is the service that monitors and manages background services through gevent greenlets.
    +Each service defines an interval to define the period of the service and defines also a job method that is run each period.
    +Any service should:
    +- Inherit from `BackgroundService` class defined here: `from jumpscale.tools.servicemanager.servicemanager import BackgroundService`
    +- Define an interval in the constructor
    +- Implement the abstract `job` method of the `BackgroundService` base class.
    +
    +### How it schedules services
    +
    +The service manager uses gevent greenlets to run jobs. It spawns the job in a greenlet after its interval period.
    +Rescheduling the service job is done in by linking a callback to the greenlet which is run after the greenlet finishes.
    +After the greenlet finishes execution the callback is fired which schedules the job to be run again after another interval.
    +
    +To add a service to the service manager you should call the `add_service` method which takes the package name and package path as parameters.
    +It loads the file in this path as a module and gets the service object defined in the service.py file.
    +
    +### Immediately schedule service
    +
    +The services first start will be after the requested interval.
    +If you need to make the service first interval immediately on server start, It can be added in your service by adding
    +
    +```python3
    +self.schedule_on_start = True
    +```
    +
    +in your service init after calling super() init.
    +
    +
    +### Example service
    +
    +```python3
    +from jumpscale.tools.servicemanager.servicemanager import BackgroundService
    +
    +class TestService(BackgroundService):
    +    def __init__(self, interval="* * * * *", *args, **kwargs):
    +        '''
    +            Test service that runs every 1 minute
    +        '''
    +        super().__init__(interval, *args, **kwargs)
    +        self.schedule_on_start = True # immediately schedule the service (optional step and the line can be removed, default=False)
    +
    +    def job(self):
    +        print("[Test Service] Done")
    +
    +service = TestService()
    +```
    +"""
    +
    +from numbers import Real
    +from math import ceil
    +from abc import ABC, abstractmethod
    +from signal import SIGTERM, SIGKILL
    +from crontab import CronTab
    +import gevent
    +from gevent.pool import Pool
    +
    +from jumpscale.loader import j
    +from jumpscale.core.base import Base
    +from multiprocessing import cpu_count
    +
    +
    +class BackgroundService(ABC):
    +    def __init__(self, interval=60, *args, **kwargs):
    +        """Abstract base class for background services managed by the service manager
    +
    +        Arguments:
    +            interval (int | CronTab object | str): scheduled job is executed every interval in seconds / CronTab object / CronTab-formatted string
    +        """
    +        self.interval = interval
    +        self.schedule_on_start = False
    +
    +    @abstractmethod
    +    def job(self):
    +        """
    +        Background job to be scheduled.
    +        Should be implemented by any service that inherits from this class
    +        """
    +        pass
    +
    +
    +# TODO: add support for non-periodic tasks
    +# TODO: configurable services
    +# TODO: add support for services not in packages
    +class ServiceManager(Base):
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.services = {}  # service objects
    +        self._scheduled = {}  # greenlets of scheduled services
    +        self._running = {}  # greenlets of currently running services
    +        self._pool = Pool(cpu_count())
    +
    +    @staticmethod
    +    def seconds_to_next_interval(interval):
    +        """Helper method to get seconds remaining to next_interval
    +
    +        Arguments:
    +            interval (Any): next interval to which seconds remaining is calculated
    +
    +        Returns:
    +            Real: number of seconds remaining until next interval
    +
    +        Raises:
    +            ValueError: when the type of interval is not Real / CronTab object / CronTab string format
    +        """
    +        if isinstance(interval, Real):
    +            return interval
    +        elif isinstance(interval, str):
    +            try:
    +                return CronTab(interval).next(default_utc=True)
    +            except Exception as e:
    +                raise j.exceptions.Value(str(e))
    +        elif isinstance(interval, CronTab):
    +            return interval.next(default_utc=True)
    +        else:
    +            raise j.exceptions.Runtime(f"Unsupported interval type: {type(interval)}")
    +
    +    @staticmethod
    +    def _load_service(path):
    +        """Load the module in the service file path and get the service object
    +
    +        Arguments:
    +            path (str): path of the service file
    +
    +        Returns:
    +            service: service object defined in the service file
    +        """
    +        module = j.tools.codeloader.load_python_module(path, force_reload=True)
    +        return module.service
    +
    +    @staticmethod
    +    def __on_exception(greenlet):
    +        """Callback to handle exception raised by service greenlet
    +
    +        Arguments:
    +            greenlet (Greenlet): greenlet object
    +        """
    +        message = f"Service {greenlet.service.name} raised an exception: {greenlet.exception}"
    +        j.tools.alerthandler.alert_raise(app_name="servicemanager", message=message, alert_type="exception")
    +        j.logger.exception(f"Service {greenlet.service.name} raised an exception", exception=greenlet.exception)
    +
    +    def __callback(self, greenlet):
    +        """Callback runs after greenlet finishes execution
    +
    +        Arguments:
    +            greenlet (Greenlet): greenlet object
    +        """
    +        greenlet.unlink(self.__callback)
    +        if greenlet.service.name in self._running:
    +            self._running.pop(greenlet.service.name)
    +
    +    def _schedule_service(self, service):
    +        """Runs a service job and schedules it to run again every period (interval) specified by the service
    +
    +        Arguments:
    +            service (BackgroundService): background service object
    +        """
    +        if service.name not in self._running:
    +            greenlet = self._pool.apply_async(service.job)
    +            greenlet.link(self.__callback)
    +            greenlet.link_exception(self.__on_exception)
    +            greenlet.start()
    +            self._running[service.name] = greenlet
    +            self._running[service.name].service = service
    +        next_start = ceil(self.seconds_to_next_interval(service.interval))
    +        self._scheduled[service.name] = gevent.spawn_later(next_start, self._schedule_service, service=service)
    +
    +    def start(self):
    +        """Start the service manager and schedule default services
    +        """
    +        # schedule default services
    +        for service in self.services.values():
    +            next_start = ceil(self.seconds_to_next_interval(service.interval))
    +            self._scheduled[service.name] = gevent.spawn_later(next_start, self._schedule_service, service=service)
    +
    +    def stop(self):
    +        """Stop all background services
    +        """
    +        j.logger.info("Stopping background services")
    +        for service in list(self.services.keys()):
    +            self.stop_service(service)
    +        j.logger.info("Done stopping the background services")
    +
    +    def add_service(self, service_name, service_path):
    +        """Add a new background service to be managed and scheduled by the service manager
    +
    +        Arguments:
    +            service_path (str): absolute path of the service file
    +        """
    +
    +        service = self._load_service(service_path)
    +        service.name = service_name
    +
    +        if service in self.services.values():
    +            j.logger.debug(f"Service {service.name} is already running. Reloading...")
    +            self.stop_service(service.name)
    +
    +        next_start = 0 if service.schedule_on_start else ceil(self.seconds_to_next_interval(service.interval))
    +        self._scheduled[service.name] = gevent.spawn_later(next_start, self._schedule_service, service=service)
    +        self.services[service.name] = service
    +        j.logger.debug(f"Service {service.name} is added.")
    +
    +    def stop_service(self, service_name, block=True):
    +        """Stop a running background service and unschedule it if it's scheduled to run again
    +
    +        Arguments:
    +            service_name (str): name of the service to be stopped
    +            block (bool): wait for service job to finish. if False, service job will be killed without waiting
    +        """
    +        if service_name not in self.services:
    +            raise j.exceptions.Value(f"Service {service_name} is not running")
    +
    +        # unschedule service if it's scheduled to run again
    +        if service_name in self._scheduled:
    +            greenlet = self._scheduled[service_name]
    +            greenlet.unlink(self.__callback)
    +            greenlet.kill()
    +            if not greenlet.dead:
    +                raise j.exceptions.Runtime("Failed to unschedule greenlet")
    +            self._scheduled.pop(service_name)
    +
    +        # wait for service to finish if it's already running
    +        if service_name in self._running:
    +            greenlet = self._running[service_name]
    +            greenlet.unlink(self.__callback)
    +            if block:
    +                try:
    +                    j.logger.info(f"Waiting the service {service_name} to finish")
    +                    greenlet.join()
    +                    j.logger.info(f"Done waiting the service {service_name}")
    +                except Exception as e:
    +                    raise j.exceptions.Runtime(f"Exception on waiting for greenlet: {str(e)}")
    +            else:
    +                try:
    +                    greenlet.kill()
    +                except Exception as e:
    +                    raise j.exceptions.Runtime(f"Exception on killing greenlet: {str(e)}")
    +                if not greenlet.dead:
    +                    raise j.exceptions.Runtime("Failed to kill running greenlet")
    +
    +        self.services.pop(service_name)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class BackgroundService +(interval=60, *args, **kwargs) +
    +
    +

    Helper class that provides a standard way to create an ABC using +inheritance.

    +

    Abstract base class for background services managed by the service manager

    +

    Arguments

    +

    interval (int | CronTab object | str): scheduled job is executed every interval in seconds / CronTab object / CronTab-formatted string

    +
    + +Expand source code + +
    class BackgroundService(ABC):
    +    def __init__(self, interval=60, *args, **kwargs):
    +        """Abstract base class for background services managed by the service manager
    +
    +        Arguments:
    +            interval (int | CronTab object | str): scheduled job is executed every interval in seconds / CronTab object / CronTab-formatted string
    +        """
    +        self.interval = interval
    +        self.schedule_on_start = False
    +
    +    @abstractmethod
    +    def job(self):
    +        """
    +        Background job to be scheduled.
    +        Should be implemented by any service that inherits from this class
    +        """
    +        pass
    +
    +

    Ancestors

    +
      +
    • abc.ABC
    • +
    +

    Subclasses

    + +

    Methods

    +
    +
    +def job(self) +
    +
    +

    Background job to be scheduled. +Should be implemented by any service that inherits from this class

    +
    + +Expand source code + +
    @abstractmethod
    +def job(self):
    +    """
    +    Background job to be scheduled.
    +    Should be implemented by any service that inherits from this class
    +    """
    +    pass
    +
    +
    +
    +
    +
    +class ServiceManager +(*args, **kwargs) +
    +
    +

    A simple attribute-based namespace.

    +

    SimpleNamespace(**kwargs)

    +

    base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

    +

    any instance can have an optional name and a parent.

    +
    class Person(Base):
    +    name = fields.String()
    +    age = fields.Float()
    +
    +p = Person(name="ahmed", age="19")
    +print(p.name, p.age)
    +
    +

    Args

    +
    +
    parent_ : Base, optional
    +
    parent instance. Defaults to None.
    +
    instance_name_ : str, optional
    +
    instance name. Defaults to None.
    +
    **values
    +
    any given field values to initiate the instance with
    +
    +
    + +Expand source code + +
    class ServiceManager(Base):
    +    def __init__(self, *args, **kwargs):
    +        super().__init__(*args, **kwargs)
    +        self.services = {}  # service objects
    +        self._scheduled = {}  # greenlets of scheduled services
    +        self._running = {}  # greenlets of currently running services
    +        self._pool = Pool(cpu_count())
    +
    +    @staticmethod
    +    def seconds_to_next_interval(interval):
    +        """Helper method to get seconds remaining to next_interval
    +
    +        Arguments:
    +            interval (Any): next interval to which seconds remaining is calculated
    +
    +        Returns:
    +            Real: number of seconds remaining until next interval
    +
    +        Raises:
    +            ValueError: when the type of interval is not Real / CronTab object / CronTab string format
    +        """
    +        if isinstance(interval, Real):
    +            return interval
    +        elif isinstance(interval, str):
    +            try:
    +                return CronTab(interval).next(default_utc=True)
    +            except Exception as e:
    +                raise j.exceptions.Value(str(e))
    +        elif isinstance(interval, CronTab):
    +            return interval.next(default_utc=True)
    +        else:
    +            raise j.exceptions.Runtime(f"Unsupported interval type: {type(interval)}")
    +
    +    @staticmethod
    +    def _load_service(path):
    +        """Load the module in the service file path and get the service object
    +
    +        Arguments:
    +            path (str): path of the service file
    +
    +        Returns:
    +            service: service object defined in the service file
    +        """
    +        module = j.tools.codeloader.load_python_module(path, force_reload=True)
    +        return module.service
    +
    +    @staticmethod
    +    def __on_exception(greenlet):
    +        """Callback to handle exception raised by service greenlet
    +
    +        Arguments:
    +            greenlet (Greenlet): greenlet object
    +        """
    +        message = f"Service {greenlet.service.name} raised an exception: {greenlet.exception}"
    +        j.tools.alerthandler.alert_raise(app_name="servicemanager", message=message, alert_type="exception")
    +        j.logger.exception(f"Service {greenlet.service.name} raised an exception", exception=greenlet.exception)
    +
    +    def __callback(self, greenlet):
    +        """Callback runs after greenlet finishes execution
    +
    +        Arguments:
    +            greenlet (Greenlet): greenlet object
    +        """
    +        greenlet.unlink(self.__callback)
    +        if greenlet.service.name in self._running:
    +            self._running.pop(greenlet.service.name)
    +
    +    def _schedule_service(self, service):
    +        """Runs a service job and schedules it to run again every period (interval) specified by the service
    +
    +        Arguments:
    +            service (BackgroundService): background service object
    +        """
    +        if service.name not in self._running:
    +            greenlet = self._pool.apply_async(service.job)
    +            greenlet.link(self.__callback)
    +            greenlet.link_exception(self.__on_exception)
    +            greenlet.start()
    +            self._running[service.name] = greenlet
    +            self._running[service.name].service = service
    +        next_start = ceil(self.seconds_to_next_interval(service.interval))
    +        self._scheduled[service.name] = gevent.spawn_later(next_start, self._schedule_service, service=service)
    +
    +    def start(self):
    +        """Start the service manager and schedule default services
    +        """
    +        # schedule default services
    +        for service in self.services.values():
    +            next_start = ceil(self.seconds_to_next_interval(service.interval))
    +            self._scheduled[service.name] = gevent.spawn_later(next_start, self._schedule_service, service=service)
    +
    +    def stop(self):
    +        """Stop all background services
    +        """
    +        j.logger.info("Stopping background services")
    +        for service in list(self.services.keys()):
    +            self.stop_service(service)
    +        j.logger.info("Done stopping the background services")
    +
    +    def add_service(self, service_name, service_path):
    +        """Add a new background service to be managed and scheduled by the service manager
    +
    +        Arguments:
    +            service_path (str): absolute path of the service file
    +        """
    +
    +        service = self._load_service(service_path)
    +        service.name = service_name
    +
    +        if service in self.services.values():
    +            j.logger.debug(f"Service {service.name} is already running. Reloading...")
    +            self.stop_service(service.name)
    +
    +        next_start = 0 if service.schedule_on_start else ceil(self.seconds_to_next_interval(service.interval))
    +        self._scheduled[service.name] = gevent.spawn_later(next_start, self._schedule_service, service=service)
    +        self.services[service.name] = service
    +        j.logger.debug(f"Service {service.name} is added.")
    +
    +    def stop_service(self, service_name, block=True):
    +        """Stop a running background service and unschedule it if it's scheduled to run again
    +
    +        Arguments:
    +            service_name (str): name of the service to be stopped
    +            block (bool): wait for service job to finish. if False, service job will be killed without waiting
    +        """
    +        if service_name not in self.services:
    +            raise j.exceptions.Value(f"Service {service_name} is not running")
    +
    +        # unschedule service if it's scheduled to run again
    +        if service_name in self._scheduled:
    +            greenlet = self._scheduled[service_name]
    +            greenlet.unlink(self.__callback)
    +            greenlet.kill()
    +            if not greenlet.dead:
    +                raise j.exceptions.Runtime("Failed to unschedule greenlet")
    +            self._scheduled.pop(service_name)
    +
    +        # wait for service to finish if it's already running
    +        if service_name in self._running:
    +            greenlet = self._running[service_name]
    +            greenlet.unlink(self.__callback)
    +            if block:
    +                try:
    +                    j.logger.info(f"Waiting the service {service_name} to finish")
    +                    greenlet.join()
    +                    j.logger.info(f"Done waiting the service {service_name}")
    +                except Exception as e:
    +                    raise j.exceptions.Runtime(f"Exception on waiting for greenlet: {str(e)}")
    +            else:
    +                try:
    +                    greenlet.kill()
    +                except Exception as e:
    +                    raise j.exceptions.Runtime(f"Exception on killing greenlet: {str(e)}")
    +                if not greenlet.dead:
    +                    raise j.exceptions.Runtime("Failed to kill running greenlet")
    +
    +        self.services.pop(service_name)
    +
    +

    Ancestors

    +
      +
    • Base
    • +
    • types.SimpleNamespace
    • +
    +

    Static methods

    +
    +
    +def seconds_to_next_interval(interval) +
    +
    +

    Helper method to get seconds remaining to next_interval

    +

    Arguments

    +

    interval (Any): next interval to which seconds remaining is calculated

    +

    Returns

    +
    +
    Real
    +
    number of seconds remaining until next interval
    +
    +

    Raises

    +
    +
    ValueError
    +
    when the type of interval is not Real / CronTab object / CronTab string format
    +
    +
    + +Expand source code + +
    @staticmethod
    +def seconds_to_next_interval(interval):
    +    """Helper method to get seconds remaining to next_interval
    +
    +    Arguments:
    +        interval (Any): next interval to which seconds remaining is calculated
    +
    +    Returns:
    +        Real: number of seconds remaining until next interval
    +
    +    Raises:
    +        ValueError: when the type of interval is not Real / CronTab object / CronTab string format
    +    """
    +    if isinstance(interval, Real):
    +        return interval
    +    elif isinstance(interval, str):
    +        try:
    +            return CronTab(interval).next(default_utc=True)
    +        except Exception as e:
    +            raise j.exceptions.Value(str(e))
    +    elif isinstance(interval, CronTab):
    +        return interval.next(default_utc=True)
    +    else:
    +        raise j.exceptions.Runtime(f"Unsupported interval type: {type(interval)}")
    +
    +
    +
    +

    Methods

    +
    +
    +def add_service(self, service_name, service_path) +
    +
    +

    Add a new background service to be managed and scheduled by the service manager

    +

    Arguments

    +

    service_path (str): absolute path of the service file

    +
    + +Expand source code + +
    def add_service(self, service_name, service_path):
    +    """Add a new background service to be managed and scheduled by the service manager
    +
    +    Arguments:
    +        service_path (str): absolute path of the service file
    +    """
    +
    +    service = self._load_service(service_path)
    +    service.name = service_name
    +
    +    if service in self.services.values():
    +        j.logger.debug(f"Service {service.name} is already running. Reloading...")
    +        self.stop_service(service.name)
    +
    +    next_start = 0 if service.schedule_on_start else ceil(self.seconds_to_next_interval(service.interval))
    +    self._scheduled[service.name] = gevent.spawn_later(next_start, self._schedule_service, service=service)
    +    self.services[service.name] = service
    +    j.logger.debug(f"Service {service.name} is added.")
    +
    +
    +
    +def start(self) +
    +
    +

    Start the service manager and schedule default services

    +
    + +Expand source code + +
    def start(self):
    +    """Start the service manager and schedule default services
    +    """
    +    # schedule default services
    +    for service in self.services.values():
    +        next_start = ceil(self.seconds_to_next_interval(service.interval))
    +        self._scheduled[service.name] = gevent.spawn_later(next_start, self._schedule_service, service=service)
    +
    +
    +
    +def stop(self) +
    +
    +

    Stop all background services

    +
    + +Expand source code + +
    def stop(self):
    +    """Stop all background services
    +    """
    +    j.logger.info("Stopping background services")
    +    for service in list(self.services.keys()):
    +        self.stop_service(service)
    +    j.logger.info("Done stopping the background services")
    +
    +
    +
    +def stop_service(self, service_name, block=True) +
    +
    +

    Stop a running background service and unschedule it if it's scheduled to run again

    +

    Arguments

    +

    service_name (str): name of the service to be stopped +block (bool): wait for service job to finish. if False, service job will be killed without waiting

    +
    + +Expand source code + +
    def stop_service(self, service_name, block=True):
    +    """Stop a running background service and unschedule it if it's scheduled to run again
    +
    +    Arguments:
    +        service_name (str): name of the service to be stopped
    +        block (bool): wait for service job to finish. if False, service job will be killed without waiting
    +    """
    +    if service_name not in self.services:
    +        raise j.exceptions.Value(f"Service {service_name} is not running")
    +
    +    # unschedule service if it's scheduled to run again
    +    if service_name in self._scheduled:
    +        greenlet = self._scheduled[service_name]
    +        greenlet.unlink(self.__callback)
    +        greenlet.kill()
    +        if not greenlet.dead:
    +            raise j.exceptions.Runtime("Failed to unschedule greenlet")
    +        self._scheduled.pop(service_name)
    +
    +    # wait for service to finish if it's already running
    +    if service_name in self._running:
    +        greenlet = self._running[service_name]
    +        greenlet.unlink(self.__callback)
    +        if block:
    +            try:
    +                j.logger.info(f"Waiting the service {service_name} to finish")
    +                greenlet.join()
    +                j.logger.info(f"Done waiting the service {service_name}")
    +            except Exception as e:
    +                raise j.exceptions.Runtime(f"Exception on waiting for greenlet: {str(e)}")
    +        else:
    +            try:
    +                greenlet.kill()
    +            except Exception as e:
    +                raise j.exceptions.Runtime(f"Exception on killing greenlet: {str(e)}")
    +            if not greenlet.dead:
    +                raise j.exceptions.Runtime("Failed to kill running greenlet")
    +
    +    self.services.pop(service_name)
    +
    +
    +
    +

    Inherited members

    + +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/startupcmd/index.html b/docs/api/jumpscale/tools/startupcmd/index.html index e64ec1a88..e33fbeea4 100644 --- a/docs/api/jumpscale/tools/startupcmd/index.html +++ b/docs/api/jumpscale/tools/startupcmd/index.html @@ -96,4 +96,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/startupcmd/startupcmd.html b/docs/api/jumpscale/tools/startupcmd/startupcmd.html index d826eacfb..e716e44b2 100644 --- a/docs/api/jumpscale/tools/startupcmd/startupcmd.html +++ b/docs/api/jumpscale/tools/startupcmd/startupcmd.html @@ -1311,4 +1311,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/syncer/index.html b/docs/api/jumpscale/tools/syncer/index.html index 56704ff96..445539f20 100644 --- a/docs/api/jumpscale/tools/syncer/index.html +++ b/docs/api/jumpscale/tools/syncer/index.html @@ -770,4 +770,4 @@

    pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/tfgateway/index.html b/docs/api/jumpscale/tools/tfgateway/index.html new file mode 100644 index 000000000..6cb2a3142 --- /dev/null +++ b/docs/api/jumpscale/tools/tfgateway/index.html @@ -0,0 +1,425 @@ + + + + + + +jumpscale.tools.tfgateway API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.tfgateway

    +
    +
    +
    + +Expand source code + +
    from jumpscale.loader import j
    +
    +from ipaddress import IPv4Address, IPv6Address
    +
    +# TODO: fixme when ipaddr is primitive in types.
    +def addr_check(addr):
    +    try:
    +        IPv4Address(addr)
    +    except:
    +        try:
    +            IPv6Address(addr)
    +        except:
    +            return False
    +        else:
    +            return True
    +    else:
    +        return True
    +
    +"""
    +j.tools.tf_gateway.tcpservice_register("bing", "www.bing.com", "122.124.214.21")
    +j.tools.tf_gateway.domain_register_a("ahmed", "bots.grid.tf.", "123.3.23.54")
    +
    +"""
    +
    +def local_redis():
    +    local = None
    +    try:
    +        local = j.clients.redis.get('local')
    +    except:
    +        local = j.clients.redis.new('local')
    +
    +    return local
    +
    +def tcpservice_register(service_name, domain, service_endpoint):
    +    """
    +    register a tcpservice to be used by tcprouter in local_redis()
    +
    +    :param service_name: service name to register in tcprouter
    +    :type service_name: str
    +    :param domain: (Server Name Indicator SNI) (e.g www.facebook.com)
    +    :type domain: str
    +    :param service_endpoint: TLS endpoint 102.142.96.34:443 "ip:port"
    +    :type service_endpoint: string
    +    """
    +    service = {}
    +    service["Key"] = "/tcprouter/service/{}".format(service_name)
    +    record = {"addr": service_endpoint, "sni": domain, "name": service_name}
    +    json_dumped_record_bytes = j.data.serializers.json.dumps(record).encode()
    +    b64_record = j.data.serializers.base64.encode(json_dumped_record_bytes).decode()
    +    service["Value"] = b64_record
    +    local_redis().set(service["Key"], j.data.serializers.json.dumps(service))
    +
    +def domain_register(threebot_name, bots_domain="bots.grid.tf.", record_type="a", records=None):
    +    """registers domain in coredns (needs to be authoritative)
    +
    +    e.g: ahmed.bots.grid.tf
    +
    +    requires nameserver on bots.grid.tf (authoritative)
    +    - ahmed is threebot_name
    +    - bots_domain is bots.grid.tf
    +
    +    :param threebot_name: threebot_name
    +    :type threebot_name: str
    +    :param bots_domain: str, defaults to "bots.grid.tf."
    +    :type bots_domain: str, optional
    +    :param record_type: valid dns record (a, aaaa, txt, srv..), defaults to "a"
    +    :type record_type: str, optional
    +    :param records: records list, defaults to None
    +    :type records: [type], optional is [ {"ip":machine ip}] in case of a/aaaa records
    +    """
    +    if not bots_domain.endswith("."):
    +        bots_domain += "."
    +    data = {}
    +    records = records or []
    +    if local_redis().hexists(bots_domain, threebot_name):
    +        data = j.data.serializers.json.loads(local_redis().hget(bots_domain, threebot_name))
    +
    +    if record_type in data:
    +        records.extend(data[record_type])
    +    data[record_type] = records
    +    local_redis().hset(bots_domain, threebot_name, j.data.serializers.json.dumps(data))
    +
    +def domain_register_a(name, domain, record_ip):
    +    """registers A domain in coredns (needs to be authoritative)
    +
    +    e.g: ahmed.bots.grid.tf
    +
    +    requires nameserver on bots.grid.tf (authoritative)
    +    - ahmed is threebot_name
    +    - bots_domain is bots.grid.tf
    +
    +    :param threebot_name: myhost
    +    :type threebot_name: str
    +    :param bots_domain: str, defaults to "grid.tf."
    +    :type bots_domain: str, optional
    +    :param record_ip: machine ip in ipv4 format
    +    :type record_ip: str
    +    """
    +    if addr_check(record_ip):
    +        return domain_register(name, domain, record_type="a", records=[{"ip": record_ip}])
    +    else:
    +        raise j.exceptions.Value("invalid ip {record_ip}".format(**locals()))
    +
    +def domain_register_aaaa(threebot_name, bots_domain, record_ip):
    +    """registers A domain in coredns (needs to be authoritative)
    +
    +    e.g: ahmed.bots.grid.tf
    +
    +    requires nameserver on bots.grid.tf (authoritative)
    +    - ahmed is threebot_name
    +    - bots_domain is bots.grid.tf
    +
    +    :param threebot_name: threebot_name
    +    :type threebot_name: str
    +    :param bots_domain: str, defaults to "bots.grid.tf."
    +    :type bots_domain: str, optional
    +    :param record_ip: machine ip in ipv6 format
    +    :type record_ip: str
    +    """
    +    if addr_check(record_ip):
    +        return domain_register(threebot_name, bots_domain, record_type="aaaa", records=[{"ip": record_ip}])
    +    else:
    +        raise j.exceptions.Value("invalid ip {record_ip}".format(**locals()))
    +
    +
    +def test():
    +    domain_register_a("ns", "3bot.me", "134.209.90.92")
    +    domain_register_a("a", "3bot.me", "134.209.90.92")
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def addr_check(addr) +
    +
    +
    +
    + +Expand source code + +
    def addr_check(addr):
    +    try:
    +        IPv4Address(addr)
    +    except:
    +        try:
    +            IPv6Address(addr)
    +        except:
    +            return False
    +        else:
    +            return True
    +    else:
    +        return True
    +
    +
    +
    +def domain_register(threebot_name, bots_domain='bots.grid.tf.', record_type='a', records=None) +
    +
    +

    registers domain in coredns (needs to be authoritative)

    +

    e.g: ahmed.bots.grid.tf

    +

    requires nameserver on bots.grid.tf (authoritative) +- ahmed is threebot_name +- bots_domain is bots.grid.tf

    +

    :param threebot_name: threebot_name +:type threebot_name: str +:param bots_domain: str, defaults to "bots.grid.tf." +:type bots_domain: str, optional +:param record_type: valid dns record (a, aaaa, txt, srv..), defaults to "a" +:type record_type: str, optional +:param records: records list, defaults to None +:type records: [type], optional is [ {"ip":machine ip}] in case of a/aaaa records

    +
    + +Expand source code + +
    def domain_register(threebot_name, bots_domain="bots.grid.tf.", record_type="a", records=None):
    +    """registers domain in coredns (needs to be authoritative)
    +
    +    e.g: ahmed.bots.grid.tf
    +
    +    requires nameserver on bots.grid.tf (authoritative)
    +    - ahmed is threebot_name
    +    - bots_domain is bots.grid.tf
    +
    +    :param threebot_name: threebot_name
    +    :type threebot_name: str
    +    :param bots_domain: str, defaults to "bots.grid.tf."
    +    :type bots_domain: str, optional
    +    :param record_type: valid dns record (a, aaaa, txt, srv..), defaults to "a"
    +    :type record_type: str, optional
    +    :param records: records list, defaults to None
    +    :type records: [type], optional is [ {"ip":machine ip}] in case of a/aaaa records
    +    """
    +    if not bots_domain.endswith("."):
    +        bots_domain += "."
    +    data = {}
    +    records = records or []
    +    if local_redis().hexists(bots_domain, threebot_name):
    +        data = j.data.serializers.json.loads(local_redis().hget(bots_domain, threebot_name))
    +
    +    if record_type in data:
    +        records.extend(data[record_type])
    +    data[record_type] = records
    +    local_redis().hset(bots_domain, threebot_name, j.data.serializers.json.dumps(data))
    +
    +
    +
    +def domain_register_a(name, domain, record_ip) +
    +
    +

    registers A domain in coredns (needs to be authoritative)

    +

    e.g: ahmed.bots.grid.tf

    +

    requires nameserver on bots.grid.tf (authoritative) +- ahmed is threebot_name +- bots_domain is bots.grid.tf

    +

    :param threebot_name: myhost +:type threebot_name: str +:param bots_domain: str, defaults to "grid.tf." +:type bots_domain: str, optional +:param record_ip: machine ip in ipv4 format +:type record_ip: str

    +
    + +Expand source code + +
    def domain_register_a(name, domain, record_ip):
    +    """registers A domain in coredns (needs to be authoritative)
    +
    +    e.g: ahmed.bots.grid.tf
    +
    +    requires nameserver on bots.grid.tf (authoritative)
    +    - ahmed is threebot_name
    +    - bots_domain is bots.grid.tf
    +
    +    :param threebot_name: myhost
    +    :type threebot_name: str
    +    :param bots_domain: str, defaults to "grid.tf."
    +    :type bots_domain: str, optional
    +    :param record_ip: machine ip in ipv4 format
    +    :type record_ip: str
    +    """
    +    if addr_check(record_ip):
    +        return domain_register(name, domain, record_type="a", records=[{"ip": record_ip}])
    +    else:
    +        raise j.exceptions.Value("invalid ip {record_ip}".format(**locals()))
    +
    +
    +
    +def domain_register_aaaa(threebot_name, bots_domain, record_ip) +
    +
    +

    registers A domain in coredns (needs to be authoritative)

    +

    e.g: ahmed.bots.grid.tf

    +

    requires nameserver on bots.grid.tf (authoritative) +- ahmed is threebot_name +- bots_domain is bots.grid.tf

    +

    :param threebot_name: threebot_name +:type threebot_name: str +:param bots_domain: str, defaults to "bots.grid.tf." +:type bots_domain: str, optional +:param record_ip: machine ip in ipv6 format +:type record_ip: str

    +
    + +Expand source code + +
    def domain_register_aaaa(threebot_name, bots_domain, record_ip):
    +    """registers A domain in coredns (needs to be authoritative)
    +
    +    e.g: ahmed.bots.grid.tf
    +
    +    requires nameserver on bots.grid.tf (authoritative)
    +    - ahmed is threebot_name
    +    - bots_domain is bots.grid.tf
    +
    +    :param threebot_name: threebot_name
    +    :type threebot_name: str
    +    :param bots_domain: str, defaults to "bots.grid.tf."
    +    :type bots_domain: str, optional
    +    :param record_ip: machine ip in ipv6 format
    +    :type record_ip: str
    +    """
    +    if addr_check(record_ip):
    +        return domain_register(threebot_name, bots_domain, record_type="aaaa", records=[{"ip": record_ip}])
    +    else:
    +        raise j.exceptions.Value("invalid ip {record_ip}".format(**locals()))
    +
    +
    +
    +def local_redis() +
    +
    +
    +
    + +Expand source code + +
    def local_redis():
    +    local = None
    +    try:
    +        local = j.clients.redis.get('local')
    +    except:
    +        local = j.clients.redis.new('local')
    +
    +    return local
    +
    +
    +
    +def tcpservice_register(service_name, domain, service_endpoint) +
    +
    +

    register a tcpservice to be used by tcprouter in local_redis()

    +

    :param service_name: service name to register in tcprouter +:type service_name: str +:param domain: (Server Name Indicator SNI) (e.g www.facebook.com) +:type domain: str +:param service_endpoint: TLS endpoint 102.142.96.34:443 "ip:port" +:type service_endpoint: string

    +
    + +Expand source code + +
    def tcpservice_register(service_name, domain, service_endpoint):
    +    """
    +    register a tcpservice to be used by tcprouter in local_redis()
    +
    +    :param service_name: service name to register in tcprouter
    +    :type service_name: str
    +    :param domain: (Server Name Indicator SNI) (e.g www.facebook.com)
    +    :type domain: str
    +    :param service_endpoint: TLS endpoint 102.142.96.34:443 "ip:port"
    +    :type service_endpoint: string
    +    """
    +    service = {}
    +    service["Key"] = "/tcprouter/service/{}".format(service_name)
    +    record = {"addr": service_endpoint, "sni": domain, "name": service_name}
    +    json_dumped_record_bytes = j.data.serializers.json.dumps(record).encode()
    +    b64_record = j.data.serializers.base64.encode(json_dumped_record_bytes).decode()
    +    service["Value"] = b64_record
    +    local_redis().set(service["Key"], j.data.serializers.json.dumps(service))
    +
    +
    +
    +def test() +
    +
    +
    +
    + +Expand source code + +
    def test():
    +    domain_register_a("ns", "3bot.me", "134.209.90.92")
    +    domain_register_a("a", "3bot.me", "134.209.90.92")
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/timer/index.html b/docs/api/jumpscale/tools/timer/index.html index 460aa9259..53e9e93e2 100644 --- a/docs/api/jumpscale/tools/timer/index.html +++ b/docs/api/jumpscale/tools/timer/index.html @@ -152,4 +152,4 @@

    Index

    Generated by pdoc 0.10.0.

    - + \ No newline at end of file diff --git a/docs/api/jumpscale/tools/wireguard/index.html b/docs/api/jumpscale/tools/wireguard/index.html new file mode 100644 index 000000000..8be8d2b64 --- /dev/null +++ b/docs/api/jumpscale/tools/wireguard/index.html @@ -0,0 +1,170 @@ + + + + + + +jumpscale.tools.wireguard API documentation + + + + + + + + + + + +
    +
    +
    +

    Module jumpscale.tools.wireguard

    +
    +
    +
    + +Expand source code + +
    import binascii
    +from nacl import public
    +from nacl.encoding import Base64Encoder
    +from nacl.signing import VerifyKey
    +
    +
    +def generate_zos_keys(node_public_key):
    +    """Generate a new set of wireguard key pair and encrypt
    +       the private side using the public key of a 0-OS node.
    +
    +    Args:
    +        node_public_key (str): hex encoded public key of 0-OS node.
    +
    +    Returns:
    +        tuple: tuple containing 3 fields (private key, private key encrypted, public key)
    +    """
    +    wg_private = public.PrivateKey.generate()
    +    wg_public = wg_private.public_key
    +
    +    wg_private_base64 = wg_private.encode(Base64Encoder)
    +    wg_public_base64 = wg_public.encode(Base64Encoder)
    +
    +    node_public_bin = binascii.unhexlify(node_public_key)
    +    node_public = VerifyKey(node_public_bin)
    +    box = public.SealedBox(node_public.to_curve25519_public_key())
    +
    +    wg_private_encrypted = box.encrypt(wg_private_base64)
    +    wg_private_encrypted_hex = binascii.hexlify(wg_private_encrypted)
    +
    +    return (wg_private_base64.decode(), wg_private_encrypted_hex.decode(), wg_public_base64.decode())
    +
    +
    +def generate_key_pair():
    +    wg_private = public.PrivateKey.generate()
    +    wg_public = wg_private.public_key
    +
    +    wg_private_base64 = wg_private.encode(Base64Encoder)
    +    wg_public_base64 = wg_public.encode(Base64Encoder)
    +    return wg_private_base64, wg_public_base64
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def generate_key_pair() +
    +
    +
    +
    + +Expand source code + +
    def generate_key_pair():
    +    wg_private = public.PrivateKey.generate()
    +    wg_public = wg_private.public_key
    +
    +    wg_private_base64 = wg_private.encode(Base64Encoder)
    +    wg_public_base64 = wg_public.encode(Base64Encoder)
    +    return wg_private_base64, wg_public_base64
    +
    +
    +
    +def generate_zos_keys(node_public_key) +
    +
    +

    Generate a new set of wireguard key pair and encrypt +the private side using the public key of a 0-OS node.

    +

    Args

    +
    +
    node_public_key : str
    +
    hex encoded public key of 0-OS node.
    +
    +

    Returns

    +
    +
    tuple
    +
    tuple containing 3 fields (private key, private key encrypted, public key)
    +
    +
    + +Expand source code + +
    def generate_zos_keys(node_public_key):
    +    """Generate a new set of wireguard key pair and encrypt
    +       the private side using the public key of a 0-OS node.
    +
    +    Args:
    +        node_public_key (str): hex encoded public key of 0-OS node.
    +
    +    Returns:
    +        tuple: tuple containing 3 fields (private key, private key encrypted, public key)
    +    """
    +    wg_private = public.PrivateKey.generate()
    +    wg_public = wg_private.public_key
    +
    +    wg_private_base64 = wg_private.encode(Base64Encoder)
    +    wg_public_base64 = wg_public.encode(Base64Encoder)
    +
    +    node_public_bin = binascii.unhexlify(node_public_key)
    +    node_public = VerifyKey(node_public_bin)
    +    box = public.SealedBox(node_public.to_curve25519_public_key())
    +
    +    wg_private_encrypted = box.encrypt(wg_private_base64)
    +    wg_private_encrypted_hex = binascii.hexlify(wg_private_encrypted)
    +
    +    return (wg_private_base64.decode(), wg_private_encrypted_hex.decode(), wg_public_base64.decode())
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/docs/wiki/baseclasses.md b/docs/wiki/baseclasses.md index 5acdc1fec..ce81ad8a9 100644 --- a/docs/wiki/baseclasses.md +++ b/docs/wiki/baseclasses.md @@ -168,9 +168,16 @@ In your instance, you can handle data updates in many ways, from triggering a ha The following are three different ways to do it: -- [Single field updates](#single-field-updates) -- [Computed and non-stored fields](#computed-and-non-stored-fields) -- [Attribute updates and events](#attribute-updates-and-events) +- [Base classes](#base-classes) + - [Base](#base) + - [Overriding constructor for Base sub-classes](#overriding-constructor-for-base-sub-classes) + - [Stored factory](#stored-factory) + - [Fields](#fields) + - [Clients](#clients) + - [Data updates and computed fields](#data-updates-and-computed-fields) + - [Single field updates](#single-field-updates) + - [Computed and non-stored fields](#computed-and-non-stored-fields) + - [Attribute updates and events](#attribute-updates-and-events) ### Single field updates diff --git a/jumpscale/clients/redis/redis.py b/jumpscale/clients/redis/redis.py index 73e4d4d50..422752701 100644 --- a/jumpscale/clients/redis/redis.py +++ b/jumpscale/clients/redis/redis.py @@ -45,7 +45,7 @@ def redis_client(self): self.__client = Redis(self.hostname, self.port) return self.__client - + def is_running(self): try: return self.redis_client.ping() diff --git a/jumpscale/core/base/factory.py b/jumpscale/core/base/factory.py index ab001ddb4..f5f3d3477 100644 --- a/jumpscale/core/base/factory.py +++ b/jumpscale/core/base/factory.py @@ -554,7 +554,7 @@ def find_many(self, cursor_=None, limit_=None, **query): raise ValueError("at least one query parameter is required, e.g. age=10") new_cursor, count, result = self.store.find(cursor_=cursor_, limit_=limit_, **query) - return new_cursor, count, (self._get_object_from_config(data[KEY_FIELD_NAME], data) for data in result) + return (new_cursor, count, (self._get_object_from_config(data[KEY_FIELD_NAME], data) for data in result)) def list_all(self): """ diff --git a/jumpscale/core/base/store/whooshfts.py b/jumpscale/core/base/store/whooshfts.py index 54fd0aaa7..56b6846e5 100644 --- a/jumpscale/core/base/store/whooshfts.py +++ b/jumpscale/core/base/store/whooshfts.py @@ -78,9 +78,7 @@ def type_fields(self): return self.location.type._fields.items() def get_schema(self): - schema_fields = { - KEY_FIELD_NAME: fields.ID(unique=True, stored=True), - } + schema_fields = {KEY_FIELD_NAME: fields.ID(unique=True, stored=True)} for name, field in self.type_fields: field_type_name = field.__class__.__name__ diff --git a/jumpscale/core/config/__init__.py b/jumpscale/core/config/__init__.py index d085c3a95..27c9ec622 100644 --- a/jumpscale/core/config/__init__.py +++ b/jumpscale/core/config/__init__.py @@ -1 +1 @@ -from .config import * \ No newline at end of file +from .config import * diff --git a/jumpscale/core/config/config.py b/jumpscale/core/config/config.py index b90846b86..c1fba7467 100644 --- a/jumpscale/core/config/config.py +++ b/jumpscale/core/config/config.py @@ -114,10 +114,7 @@ def get_default_config(): "debug": True, "shell": "ptpython", "logging": { - "default": { - "enabled": True, - "level": 10, - }, + "default": {"enabled": True, "level": 10}, "redis": { "enabled": True, "level": 15, @@ -142,9 +139,7 @@ def get_default_config(): }, "factory": {"always_reload": False}, "store": "filesystem", - "threebot": { - "default": "", - }, + "threebot": {"default": ""}, } diff --git a/jumpscale/core/exceptions/__init__.py b/jumpscale/core/exceptions/__init__.py index d3eda5157..281c8b350 100644 --- a/jumpscale/core/exceptions/__init__.py +++ b/jumpscale/core/exceptions/__init__.py @@ -1 +1 @@ -from .exceptions import * \ No newline at end of file +from .exceptions import * diff --git a/jumpscale/core/logging/logging.py b/jumpscale/core/logging/logging.py index d16bd17da..88ed84940 100644 --- a/jumpscale/core/logging/logging.py +++ b/jumpscale/core/logging/logging.py @@ -11,13 +11,7 @@ from jumpscale.loader import j -LEVELS = { - 10: "DEBUG", - 20: "INFO", - 30: "WARNING", - 40: "ERROR", - 50: "CRITICAL", -} +LEVELS = {10: "DEBUG", 20: "INFO", 30: "WARNING", 40: "ERROR", 50: "CRITICAL"} # init is kept as a name for backward compatibility DEFAULT_APP_NAME = "init" diff --git a/jumpscale/data/bcdb/__init__.py b/jumpscale/data/bcdb/__init__.py index a814d4a55..8417e8254 100644 --- a/jumpscale/data/bcdb/__init__.py +++ b/jumpscale/data/bcdb/__init__.py @@ -1 +1 @@ -from .bcdb import * \ No newline at end of file +from .bcdb import * diff --git a/jumpscale/data/bcdb/bcdb.py b/jumpscale/data/bcdb/bcdb.py index 7477322f6..969a7735d 100644 --- a/jumpscale/data/bcdb/bcdb.py +++ b/jumpscale/data/bcdb/bcdb.py @@ -3,6 +3,7 @@ from jumpscale.data.bcdb import models as models from .clients import RedisStorageClient, RedisIndexClient, SonicIndexTextClient, SQLiteIndexSetClient + class BCDB: def __init__(self, ns): self.ns = ns @@ -10,12 +11,8 @@ def __init__(self, ns): self.indexer = RedisIndexClient(ns) self.indexer_set = SQLiteIndexSetClient(ns) self.indexer_text = SonicIndexTextClient(ns) - self.models = { - - } - self.loaded_models = { - - } + self.models = {} + self.loaded_models = {} self.detect_models() self.model_model = self.models["model"](self) @@ -25,10 +22,10 @@ def detect_models(self): model = getattr(models, model_name) if isinstance(model, type) and issubclass(model, models.ModelBase): self.models[model._name] = model - + def save_obj(self, model, obj): """Saves the given objects which belongs to model in the db and update the indexes. - + Args: model (ModelObj): The model object that obj belongs to. obj (JSObjBase): The object that will be saved. @@ -36,7 +33,7 @@ def save_obj(self, model, obj): self.indexer_set.set(model, obj) self.indexer_text.set(model, obj) for prop in model.schema.props.values(): - old_obj = model.get_by('id', obj.id) + old_obj = model.get_by("id", obj.id) prop_name = prop.name index_prop = getattr(obj, prop.name) old_index = getattr(old_obj, prop.name) if old_obj else None @@ -47,10 +44,10 @@ def save_obj(self, model, obj): def model_id_incr(self, model): """Increment the id counter in the model and returns the new id. Used to assign unique id for each created object. - + Args: model (ModelObj): The model object. - + Returns: int: The new unique id """ @@ -58,11 +55,11 @@ def model_id_incr(self, model): def get_item_by_id(self, model, id): """Gets the object in the model with the given id. - + Args: model (ModelObj): The model to be searched in. id (int): The object's id. - + Returns: JSObjBase or None: The JSObject with the given id. None if none was found. """ @@ -72,16 +69,17 @@ def get_entry(self, model, key, val): """Search for objects whose key equal val. 1. It searches in the redis index if key is indexed. 2. Else, It's searched for in the sqlite index if the key is indexed for range search. - 3. Else, All objects in the db belonging to the given model is scanned linearly to determine the matching object. - + 3. Else, All objects in the db belonging to the given model + is scanned linearly to determine the matching object. + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: JSObjBase or None: The matched object (o: o.key == val). None if none matched. """ @@ -100,17 +98,18 @@ def get_entry(self, model, key, val): def get_range(self, model, key, min, max): """Searches for objects whose key lies between min and max. - It tries to search for it in the index. If the key is not indexed it loops through all the objects. - + It tries to search for it in the index. + If the key is not indexed it loops through all the objects. + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -121,24 +120,23 @@ def get_range(self, model, key, min, max): else: result = [] for obj in self.storage.get_keys_in_model(model): - obj_val = getattr(obj, key) - if obj_val >= min and obj_val <= max: - result.append(obj) + obj_val = getattr(obj, key) + if obj_val >= min and obj_val <= max: + result.append(obj) return result - def get_item_from_index(self, model, key, val): """Search for objects whose key equal val. The key must be indexed for search. - + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. val (value): The value. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key == val) """ @@ -150,18 +148,19 @@ def get_item_from_index(self, model, key, val): return self.get_item_by_id(model, keyid) if keyid else None def get_item_from_index_set(self, model, key, min, max): - """Searches for objects whose key lies between min and max. The key must be indexed for range search. - + """Searches for objects whose key lies between min and max. + The key must be indexed for range search. + Args: model (ModelObj): The model in which the key is searched for. key (str): The model property that is checked for. min (value): The minimum. max (value): The maximum. - + Raises: RuntimeError: If the key is not a part of the schema. RuntimeError: If the key is not indexed for range search. - + Returns: List[JSObjBase]: A list of matched objects (o: o.key >= min and o.key <= max) """ @@ -170,23 +169,23 @@ def get_item_from_index_set(self, model, key, min, max): if not model.schema.props[key].index_key: raise RuntimeError(f"{key} is not indexed.") return [self.get_item_by_id(model, x[0]) for x in self.indexer_set.get(model, key, min, max)] - def get_item_from_index_text(self, model, key, pattern): - """Searches for objects whose key matches the given pattern inside model. The key must be registered in the text index. - + """Searches for objects whose key matches the given pattern inside model. + The key must be registered in the text index. + Args: model (Modelobj): The model object in which the pattern is searched. key (str): The model property that the pattern is searched for in. pattern (str): The pattern to be searched for. - + Notes: Currently sonic server matches for some patterns and doesn't for others. Raises: RuntimeError: If the key is not defined in the model. RuntimeError: If the key is not indexed for search - + Returns: list[JSObjBase]: List of matching objects (o: o.key matches pattern). """ @@ -198,13 +197,13 @@ def get_item_from_index_text(self, model, key, pattern): def get_model_by_name(self, model_name): """Returns a Model object given its name. - + Args: model_name (str): The name of the model. - + Raises: RuntimeError: Raised when no model exists with the given. - + Returns: ModelObj: The model object. """ diff --git a/jumpscale/data/bcdb/interfaces.py b/jumpscale/data/bcdb/interfaces.py index 8ce83532b..25139cc33 100644 --- a/jumpscale/data/bcdb/interfaces.py +++ b/jumpscale/data/bcdb/interfaces.py @@ -1,4 +1,3 @@ - class StorageInterface: def get(self, model, obj_id): pass @@ -12,6 +11,7 @@ def get_keys_in_model(self, model): def incr_id(self, model): pass + class IndexInterface: def __init__(self, bcdb_namespace): pass @@ -22,6 +22,7 @@ def get(self, model, index_prop, index_value): def set(self, model, index_prop, index_value, obj_id, old_value=None): pass + class IndexSetInterface: def __init__(self, bcdb_namespace): pass @@ -32,19 +33,21 @@ def get(self, model, index_prop, min, max): def set(self, model, obj): pass + class IndexTextInterface: def __init__(self, bcdb_namespace): pass def set(self, model, obj): - pass + pass def get(self, model, index_prop, pattern): - pass + pass + class SerializerInterface: def loads(self, model, s): pass def dumps(self, model, data): - pass \ No newline at end of file + pass diff --git a/jumpscale/data/bcdb/models/__init__.py b/jumpscale/data/bcdb/models/__init__.py index 088f5eea9..6857a6095 100644 --- a/jumpscale/data/bcdb/models/__init__.py +++ b/jumpscale/data/bcdb/models/__init__.py @@ -2,6 +2,7 @@ import os import importlib + def add_model(file_name): m = importlib.import_module("." + file_name[:-3], "jumpscale.data.bcdb.models") for attr in dir(m): @@ -9,7 +10,8 @@ def add_model(file_name): if isinstance(pyattr, type) and issubclass(pyattr, ModelBase): globals()[attr] = pyattr + files = os.listdir(os.path.dirname(__file__)) for f in files: if f.endswith("_model.py"): - add_model(f) \ No newline at end of file + add_model(f) diff --git a/jumpscale/data/bcdb/models/db_model.py b/jumpscale/data/bcdb/models/db_model.py index 5ee36c65b..65f2b2ffc 100644 --- a/jumpscale/data/bcdb/models/db_model.py +++ b/jumpscale/data/bcdb/models/db_model.py @@ -1,9 +1,10 @@ from .base import ModelBase + class DBModel(ModelBase): _schema = """ @url = db host = "" (S) user = (O)!user """ - _name = "db" \ No newline at end of file + _name = "db" diff --git a/jumpscale/data/bcdb/models/emplyee_model.py b/jumpscale/data/bcdb/models/emplyee_model.py index 655a8a844..14f6f4cec 100644 --- a/jumpscale/data/bcdb/models/emplyee_model.py +++ b/jumpscale/data/bcdb/models/emplyee_model.py @@ -1,5 +1,6 @@ from .base import ModelBase + class EmployeeModel(ModelBase): _schema = """ @url = employee @@ -7,4 +8,4 @@ class EmployeeModel(ModelBase): age = 0 (I) salary** = 0 (I) """ - _name = "employee" \ No newline at end of file + _name = "employee" diff --git a/jumpscale/data/bcdb/models/post_model.py b/jumpscale/data/bcdb/models/post_model.py index c8b420870..2e1724235 100644 --- a/jumpscale/data/bcdb/models/post_model.py +++ b/jumpscale/data/bcdb/models/post_model.py @@ -1,9 +1,10 @@ from .base import ModelBase + class QuoteModel(ModelBase): _schema = """ @url = quote author** = "Anonymous" (S) quote*** = "" (S) """ - _name = "quote" \ No newline at end of file + _name = "quote" diff --git a/jumpscale/data/bcdb/models/proj_model.py b/jumpscale/data/bcdb/models/proj_model.py index 3c124b6e5..9e4548a3c 100644 --- a/jumpscale/data/bcdb/models/proj_model.py +++ b/jumpscale/data/bcdb/models/proj_model.py @@ -1,5 +1,6 @@ from .base import ModelBase + class ProjModel(ModelBase): _schema = """ @url = proj @@ -7,4 +8,4 @@ class ProjModel(ModelBase): employees = 0 (I) company = "" (S) """ - _name = "proj" \ No newline at end of file + _name = "proj" diff --git a/jumpscale/data/bcdb/models/test_model.py b/jumpscale/data/bcdb/models/test_model.py index 1e3161a2f..a98072e21 100644 --- a/jumpscale/data/bcdb/models/test_model.py +++ b/jumpscale/data/bcdb/models/test_model.py @@ -1,8 +1,9 @@ from .base import ModelBase + class TestModel(ModelBase): _schema = """ @url = test a = 1 (I) """ - _name = "test" \ No newline at end of file + _name = "test" diff --git a/jumpscale/data/bcdb/models/user_model.py b/jumpscale/data/bcdb/models/user_model.py index caee14f13..ca9121a92 100644 --- a/jumpscale/data/bcdb/models/user_model.py +++ b/jumpscale/data/bcdb/models/user_model.py @@ -1,9 +1,10 @@ from .base import ModelBase + class UserModel(ModelBase): _schema = """ @url = user username = "" (S) password = "" (S) """ - _name = "user" \ No newline at end of file + _name = "user" diff --git a/jumpscale/data/cache/__init__.py b/jumpscale/data/cache/__init__.py index 082644191..7ba574977 100644 --- a/jumpscale/data/cache/__init__.py +++ b/jumpscale/data/cache/__init__.py @@ -1 +1 @@ -from functools import lru_cache \ No newline at end of file +from functools import lru_cache diff --git a/jumpscale/data/idgenerator/__init__.py b/jumpscale/data/idgenerator/__init__.py index c85855aa3..321d8b008 100644 --- a/jumpscale/data/idgenerator/__init__.py +++ b/jumpscale/data/idgenerator/__init__.py @@ -1 +1 @@ -from .idgenerator import * \ No newline at end of file +from .idgenerator import * diff --git a/jumpscale/data/inifile/inifile.py b/jumpscale/data/inifile/inifile.py index 2f19133c1..7573fb63e 100644 --- a/jumpscale/data/inifile/inifile.py +++ b/jumpscale/data/inifile/inifile.py @@ -137,4 +137,3 @@ def remove_property(self, section_name, property_name): property_key (str) : the name of the property """ self.parser.remove_option(section_name, property_name) - diff --git a/jumpscale/data/serializers/dill.py b/jumpscale/data/serializers/dill.py index 3e0001a63..a9698383c 100644 --- a/jumpscale/data/serializers/dill.py +++ b/jumpscale/data/serializers/dill.py @@ -1 +1 @@ -from dill import dumps, dump, loads, load \ No newline at end of file +from dill import dumps, dump, loads, load diff --git a/jumpscale/data/serializers/lzma.py b/jumpscale/data/serializers/lzma.py index bc937f67e..14512e53a 100644 --- a/jumpscale/data/serializers/lzma.py +++ b/jumpscale/data/serializers/lzma.py @@ -1,23 +1,25 @@ import pylzma + def compress(obj): """compress string with lzma algorithm - + Arguments: obj (string) : the string will be encoded - + Returns: bytes : the compressed bytes """ return pylzma.compress(obj) + def decompress(s): """decompress lzma bytes to original obj - + Arguments: s (bytes) : the bytes will be compressed - + Returns: (string) : the decompressed string """ - return pylzma.decompress(s) \ No newline at end of file + return pylzma.decompress(s) diff --git a/jumpscale/data/serializers/msgpack.py b/jumpscale/data/serializers/msgpack.py index b5aa15465..2667101aa 100644 --- a/jumpscale/data/serializers/msgpack.py +++ b/jumpscale/data/serializers/msgpack.py @@ -2,11 +2,11 @@ def dumps(obj): - """dump dict object into msgpack stream - + """dump dict object into msgpack stream + Arguments: - obj (dict) : the dict which will be dumped - + obj (dict) : the dict which will be dumped + Returns: string : the msgpack stream """ @@ -15,10 +15,10 @@ def dumps(obj): def loads(s): """loads the data from msgpack string into dict - + Arguments: s (string) : the msgpack stream - + Returns: dict : the loaded data from msgpack stram """ diff --git a/jumpscale/data/serializers/pickle.py b/jumpscale/data/serializers/pickle.py index 7d5feff93..3bb235f68 100644 --- a/jumpscale/data/serializers/pickle.py +++ b/jumpscale/data/serializers/pickle.py @@ -2,11 +2,11 @@ def decompress(obj): - """dump pickle bytes object into string - + """dump pickle bytes object into string + Arguments: - obj (pickle bytes) : the pickle bytes which will be dumped - + obj (pickle bytes) : the pickle bytes which will be dumped + Returns: string : the string """ @@ -15,10 +15,10 @@ def decompress(obj): def compress(obj): """loads the data from pickle string into pickle bytes - + Arguments: obj (string) : the string - + Returns: pickle bytes : the loaded data from pickle stram """ diff --git a/jumpscale/data/serializers/toml.py b/jumpscale/data/serializers/toml.py index 5d4b28292..40da3f3eb 100644 --- a/jumpscale/data/serializers/toml.py +++ b/jumpscale/data/serializers/toml.py @@ -2,11 +2,11 @@ def dumps(d): - """dump dict object into toml stream - + """dump dict object into toml stream + Arguments: - d (dict) : the dict which will be dumped - + d (dict) : the dict which will be dumped + Returns: string : the toml stream """ @@ -16,13 +16,12 @@ def dumps(d): def loads(s): """loads the data from toml string into dict - + Arguments: s (string) : the toml stream - + Returns: dict : the loaded data from toml stram """ d = pytoml.loads(s) return d - diff --git a/jumpscale/data/serializers/yaml.py b/jumpscale/data/serializers/yaml.py index 88fbfe3fb..4941f5d17 100644 --- a/jumpscale/data/serializers/yaml.py +++ b/jumpscale/data/serializers/yaml.py @@ -2,11 +2,11 @@ def dumps(obj): - """dump dict object into yaml stream - + """dump dict object into yaml stream + Arguments: - obj (dict) : the dict which will be dumped - + obj (dict) : the dict which will be dumped + Returns: string : the yaml stream """ @@ -15,12 +15,11 @@ def dumps(obj): def loads(s): """loads the data from yaml string into dict - + Arguments: s (string) : the yaml stream - + Returns: dict : the loaded data from yaml stram """ return yaml.load(s) - diff --git a/jumpscale/data/text/__init__.py b/jumpscale/data/text/__init__.py index daebc9c03..e6d323d67 100644 --- a/jumpscale/data/text/__init__.py +++ b/jumpscale/data/text/__init__.py @@ -28,10 +28,11 @@ def removeprefix(s: str, prefix: str) -> str: """ if s.startswith(prefix): - return s[len(prefix):] + return s[len(prefix) :] else: return s[:] + def removesuffix(s: str, suffix: str) -> str: """Remove a suffix string `suffix` from a string `s`. @@ -44,6 +45,6 @@ def removesuffix(s: str, suffix: str) -> str: """ if suffix and s.endswith(suffix): - return s[:-len(suffix)] + return s[: -len(suffix)] else: - return s[:] \ No newline at end of file + return s[:] diff --git a/jumpscale/loader.py b/jumpscale/loader.py index 3e69eb046..fb556c2a6 100644 --- a/jumpscale/loader.py +++ b/jumpscale/loader.py @@ -24,7 +24,7 @@ def get_container_type(full_name: str) -> type: """ cls_name = "".join([name.capitalize() for name in full_name.split(".")]) - return type(cls_name, (object,), {"__fullname": full_name,}) + return type(cls_name, (object,), {"__fullname": full_name}) def get_lazy_import_property(name, root_module, container_type): diff --git a/jumpscale/servers/openresty/server.py b/jumpscale/servers/openresty/server.py index acc773f54..26c3523cb 100644 --- a/jumpscale/servers/openresty/server.py +++ b/jumpscale/servers/openresty/server.py @@ -31,8 +31,7 @@ def path_web(self): return self.parent.path_web def configure(self): - """Writes configuration of the website and its locations - """ + """Writes configuration of the website and its locations""" j.sals.fs.mkdir(self.path_cfg_dir) config = render_config_template("website", base_dir=j.core.dirs.BASEDIR, website=self) @@ -82,13 +81,13 @@ def logs_dir(self): return self._logs_dir def configure(self): + """configures main nginx conf""" # clean old websites config self.cleanup() - """configures main nginx conf - """ + # self.install() This is commented for now until the repo and necessary deps are handled configtext = j.tools.jinja2.render_template( - template_path=j.sals.fs.join_paths(DIR_PATH, "templates", "nginx.conf"), logs_dir=self.logs_dir, + template_path=j.sals.fs.join_paths(DIR_PATH, "templates", "nginx.conf"), logs_dir=self.logs_dir ) j.sals.fs.write_file(self.path_cfg, configtext) @@ -133,9 +132,7 @@ def install(self, reset=False): # copy the templates to the right location j.sals.fs.copy_tree(f"{DIR_PATH}/web_resources/", self.path_cfg_dir) - j.sals.fs.symlink( - f"{weblibs_path}/static", f"{self.path_web}/static/weblibs", overwrite=True, - ) + j.sals.fs.symlink(f"{weblibs_path}/static", f"{self.path_web}/static/weblibs", overwrite=True) self.status = Status.INSTALLED self.save() diff --git a/jumpscale/servers/rack/__init__.py b/jumpscale/servers/rack/__init__.py index 665e649fc..8e5bccac9 100644 --- a/jumpscale/servers/rack/__init__.py +++ b/jumpscale/servers/rack/__init__.py @@ -1,4 +1,4 @@ def export_module_as(): from .rack import ServerRack - return ServerRack() + return ServerRack() diff --git a/jumpscale/tools/alerthandler/__init__.py b/jumpscale/tools/alerthandler/__init__.py index 3cb31cce2..9d6f3a339 100644 --- a/jumpscale/tools/alerthandler/__init__.py +++ b/jumpscale/tools/alerthandler/__init__.py @@ -1,3 +1,4 @@ def export_module_as(): from .alerthandler import AlertsHandler + return AlertsHandler() diff --git a/jumpscale/tools/jinja2/__init__.py b/jumpscale/tools/jinja2/__init__.py index 7c9f338a7..b21725c8a 100644 --- a/jumpscale/tools/jinja2/__init__.py +++ b/jumpscale/tools/jinja2/__init__.py @@ -40,13 +40,7 @@ """ from jumpscale.loader import j -from jinja2 import ( - Environment, - FileSystemLoader, - select_autoescape, - StrictUndefined, - Template, -) +from jinja2 import Environment, FileSystemLoader, select_autoescape, StrictUndefined, Template def get_env(templates_path): @@ -58,7 +52,7 @@ def get_env(templates_path): Returns: jinja2.Environment: Jinja2 env """ - return Environment(loader=FileSystemLoader(templates_path), autoescape=select_autoescape(["html", "xml"]),) + return Environment(loader=FileSystemLoader(templates_path), autoescape=select_autoescape(["html", "xml"])) def get_template(template_path=None, template_text=None): @@ -115,7 +109,7 @@ def render_template(template_path=None, template_text=None, dest=None, **kwargs) def render_code_python( - obj_key=None, template_path=None, template_text=None, dest=None, objForHash=None, name=None, **kwargs, + obj_key=None, template_path=None, template_text=None, dest=None, objForHash=None, name=None, **kwargs ): # TODO pass diff --git a/jumpscale/tools/keygen/keygen.py b/jumpscale/tools/keygen/keygen.py index 77e28825c..23a87182f 100644 --- a/jumpscale/tools/keygen/keygen.py +++ b/jumpscale/tools/keygen/keygen.py @@ -1 +1 @@ -print("keygen..") \ No newline at end of file +print("keygen..") diff --git a/jumpscale/tools/schemac/__init__.py b/jumpscale/tools/schemac/__init__.py index 83b636eb3..90317b06f 100644 --- a/jumpscale/tools/schemac/__init__.py +++ b/jumpscale/tools/schemac/__init__.py @@ -116,6 +116,7 @@ class DespiegkTest(Base): ``` ''' + def get_compiler(schema_text, lang="python"): from .compiler import Compiler diff --git a/pyproject.toml b/pyproject.toml index 89937fde8..f3b553cb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "js-ng" packages = [{ include = "jumpscale" }] -version = "11.0b17" +version = "11.1" description = "system automation, configuration management and RPC framework" authors = ["xmonader "] license = "MIT" diff --git a/tests/core/executors/test_local.py b/tests/core/executors/test_local.py index 011e0693e..cb67d3570 100644 --- a/tests/core/executors/test_local.py +++ b/tests/core/executors/test_local.py @@ -13,9 +13,7 @@ class TestLocal(BaseTests): - @parameterized.expand( - [(CMD_STR, True,), (CMD_LIST, False,),] - ) + @parameterized.expand([(CMD_STR, True), (CMD_LIST, False)]) def test001_execute_local_command(self, cmd, warn): self.info(f"Execute `{cmd}` as local command with warn {warn}") code, stdout, stderr = j.core.executors.run_local(cmd, warn=warn) @@ -33,9 +31,7 @@ def test003_execute_wrong_local_command_with_warn(self): self.assertEqual("", stdout) self.assertIn("random: command not found", stderr) - @parameterized.expand( - [(CMD_STR, True,), (CMD_LIST, False,),] - ) + @parameterized.expand([(CMD_STR, True), (CMD_LIST, False)]) def test004_execute_local_command_with_hide(self, cmd, hide): self.info(f"Execute command `{cmd}` with hide {hide} and assert that sout is okay") capture = io.StringIO() @@ -49,9 +45,7 @@ def test004_execute_local_command_with_hide(self, cmd, hide): else: self.assertEqual(output, stdout) - @parameterized.expand( - [(CMD_STR, True,), (CMD_LIST, False,),] - ) + @parameterized.expand([(CMD_STR, True), (CMD_LIST, False)]) def test005_execute_local_command_with_echo(self, cmd, echo): self.info(f"Execute command `{cmd}`with echo {echo} and assert that sout is okay") capture = io.StringIO() @@ -65,9 +59,7 @@ def test005_execute_local_command_with_echo(self, cmd, echo): else: self.assertNotIn(CMD_STR, output) - @parameterized.expand( - [(CMD_STR,), (CMD_LIST,),] - ) + @parameterized.expand([(CMD_STR,), (CMD_LIST,)]) def test006_execute_local_command_hide_overwrite_echo(self, cmd): self.info(f"Execute command `{cmd}` and assert that hide overwrites echo") capture = io.StringIO() diff --git a/tests/sals/fs/test_fs.py b/tests/sals/fs/test_fs.py index dc3514f53..b92489b3e 100644 --- a/tests/sals/fs/test_fs.py +++ b/tests/sals/fs/test_fs.py @@ -34,7 +34,7 @@ def create_tree(self): j.sals.fs.touch(random_file) random_files_internal.append(random_file) - return random_dir_dest, random_dir_dest_2, random_files, random_files_internal + return (random_dir_dest, random_dir_dest_2, random_files, random_files_internal) def test001_is_ascii_file(self): pass diff --git a/tests/sals/nettools/test_nettools.py b/tests/sals/nettools/test_nettools.py index 5acb6b18a..1ca70dd5b 100644 --- a/tests/sals/nettools/test_nettools.py +++ b/tests/sals/nettools/test_nettools.py @@ -330,7 +330,7 @@ def test_18_ping_machine_success(ip, timeout, allowhostname): assert result -@pytest.mark.parametrize("ip, timeout, allowhostname", [("10.200.199.198", 1, False),]) +@pytest.mark.parametrize("ip, timeout, allowhostname", [("10.200.199.198", 1, False)]) def test_19_ping_machine_timeout(ip, timeout, allowhostname): """Test case for check whether the ping_machine will timeout after specified number of seconds @@ -348,7 +348,7 @@ def test_19_ping_machine_timeout(ip, timeout, allowhostname): assert has_timed_out and is_false -@pytest.mark.parametrize("ip, timeout, allowhostname", [("www.google.com", 2, False),]) +@pytest.mark.parametrize("ip, timeout, allowhostname", [("www.google.com", 2, False)]) def test_20_ping_machine_exception(ip, timeout, allowhostname): """Test case for check whether the ping_machine will raise exception when it receive a host name while allowhostname is false @@ -363,7 +363,7 @@ def test_20_ping_machine_exception(ip, timeout, allowhostname): @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [("ftp://ftp.sas.com/techsup/download/TestSSLServer4.zip", "test_21_downloaded", None, None, True, False, False),], + [("ftp://ftp.sas.com/techsup/download/TestSSLServer4.zip", "test_21_downloaded", None, None, True, False, False)], ) def test_21_download_ftp(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from ftp server @@ -392,7 +392,7 @@ def test_21_download_ftp(url, localpath, username, passwd, overwrite, append_to_ @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [("https://statweb.stanford.edu/~jhf/ftp/README", "test_22_downloaded", None, None, True, False, False),], + [("https://statweb.stanford.edu/~jhf/ftp/README", "test_22_downloaded", None, None, True, False, False)], ) def test_22_download_https(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from https link @@ -420,7 +420,7 @@ def test_22_download_https(url, localpath, username, passwd, overwrite, append_t @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "test_23_downloaded", None, None, True, False, False),], + [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "test_23_downloaded", None, None, True, False, False)], ) def test_23_download_http(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from http link @@ -448,7 +448,7 @@ def test_23_download_http(url, localpath, username, passwd, overwrite, append_to @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [("ftp://ftp.sas.com/techsup/download/TestSSLServer4.zip", "test_24_downloaded", None, None, True, True, False),], + [("ftp://ftp.sas.com/techsup/download/TestSSLServer4.zip", "test_24_downloaded", None, None, True, True, False)], ) def test_24_download_append_to_home(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from url to localpath relative to user home directory @@ -484,7 +484,7 @@ def test_24_download_append_to_home(url, localpath, username, passwd, overwrite, True, False, False, - ), + ) ], ) def test_25_download_create_parents(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): @@ -517,7 +517,7 @@ def test_25_download_create_parents(url, localpath, username, passwd, overwrite, @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "", None, None, True, False, True),], + [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "", None, None, True, False, True)], ) def test_26_download_name_from_url(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from url to cwd and get the filename from the url @@ -542,17 +542,7 @@ def test_26_download_name_from_url(url, localpath, username, passwd, overwrite, @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [ - ( - "http://ftp.sas.com/techsup/download/TestSSLServer4.txt", - "downloaded_test_27", - None, - None, - False, - False, - False, - ), - ], + [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "downloaded_test_27", None, None, False, False, False)], ) def test_27_download_overwrite_False(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from url to localpath when the file already exists @@ -576,7 +566,7 @@ def test_27_download_overwrite_False(url, localpath, username, passwd, overwrite @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "downloaded_test_28", None, None, True, False, False),], + [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "downloaded_test_28", None, None, True, False, False)], ) def test_28_download_overwrite_True(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from url to localpath when the file already exists @@ -602,7 +592,7 @@ def test_28_download_overwrite_True(url, localpath, username, passwd, overwrite, @pytest.mark.xfail(reason="this test not work on github-hosted runner. looking into it") @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "unwriteable_dir", None, None, False, True, True),], + [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", "unwriteable_dir", None, None, False, True, True)], ) def test_29_download_to_unwritable_dir(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from url to localpath when user don't have proper Permissions @@ -636,7 +626,7 @@ def edit_write_permissions(path, permissions): @pytest.mark.parametrize( "url, localpath, username, passwd, overwrite, append_to_home, name_from_url", - [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", None, None, None, False, False, True),], + [("http://ftp.sas.com/techsup/download/TestSSLServer4.txt", None, None, None, False, False, True)], ) def test_30_download_return_content(url, localpath, username, passwd, overwrite, append_to_home, name_from_url): """Test case for download a resource from url and return the content