main > main: init
This commit is contained in:
commit
ba54939a3b
22
.coveragerc
Normal file
22
.coveragerc
Normal file
@ -0,0 +1,22 @@
|
||||
[run]
|
||||
source="."
|
||||
|
||||
[report]
|
||||
show_missing = true
|
||||
omit =
|
||||
# ignore tests folder
|
||||
tests/*
|
||||
# ignore thirdparty imports
|
||||
src/thirdparty/*
|
||||
# ignore models folder (auto generated)
|
||||
models/**
|
||||
# ignore __init__ files (only used for exports)
|
||||
**/__init__.py
|
||||
# ignore main.py, app.py (-> too macroscopic and covered by integration tests)
|
||||
main.py
|
||||
src/app.py
|
||||
# TODO: increase code-coverage:
|
||||
fail_under = 100
|
||||
precision = 1
|
||||
exclude_lines =
|
||||
pragma: no cover
|
86
.gitignore
vendored
Normal file
86
.gitignore
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
*
|
||||
!/.gitignore
|
||||
|
||||
################################################################
|
||||
# DOCKER
|
||||
################################################################
|
||||
|
||||
!/.dockerignore
|
||||
!/Dockerfile
|
||||
!/docker-compose.yaml
|
||||
|
||||
################################################################
|
||||
# MAIN FOLDER
|
||||
################################################################
|
||||
|
||||
!/justfile
|
||||
!/Makefile
|
||||
!/README.md
|
||||
!/LICENSE
|
||||
!/.coveragerc
|
||||
!/pyproject.toml
|
||||
!/requirements*
|
||||
|
||||
################################################################
|
||||
# PROJECT FILES
|
||||
################################################################
|
||||
|
||||
!/templates
|
||||
!/templates/template*
|
||||
|
||||
!/src
|
||||
!/src/**/
|
||||
!/src/**/*.py
|
||||
!/src/**/*.yaml
|
||||
!/main.py
|
||||
|
||||
!/notebooks
|
||||
!/notebooks/*.ipynb
|
||||
!/notebooks/*.md
|
||||
|
||||
# NOTE: models are created as part of the build process:
|
||||
!/models
|
||||
!/models/*.yaml
|
||||
!/models/README.md
|
||||
|
||||
!/docs
|
||||
!/docs/*/
|
||||
!/docs/*/Models/
|
||||
!/docs/**/*.md
|
||||
|
||||
!/tests
|
||||
!/tests/**/
|
||||
!/tests/**/*.py
|
||||
!/tests/resources/*.yaml
|
||||
!/tests/README.md
|
||||
|
||||
!/dist
|
||||
!/dist/VERSION
|
||||
|
||||
################################################################
|
||||
# AUXLIARY
|
||||
################################################################
|
||||
|
||||
/logs
|
||||
/**/.env
|
||||
|
||||
################################################################
|
||||
# TEMPLATES
|
||||
################################################################
|
||||
|
||||
!/templates/.env
|
||||
|
||||
################################################################
|
||||
# ARTEFACTS
|
||||
################################################################
|
||||
|
||||
/**/__pycache__
|
||||
/**/.pytest_cache
|
||||
/**/.DS_Store
|
||||
/**/__archive__*
|
||||
|
||||
################################################################
|
||||
# Git Keep
|
||||
################################################################
|
||||
|
||||
!/**/.gitkeep
|
77
README.md
Normal file
77
README.md
Normal file
@ -0,0 +1,77 @@
|
||||
# Algorithmen und Datenstrukturen I, WiSe 2022-23 #
|
||||
|
||||
Der Inhalt dieses Repository ist keineswegs verpflichtend, sondern dient dem Zweck, „Handnotizen“ anzubieten.
|
||||
Es besteht hier kein Anspruch auf Vollständigkeit.
|
||||
Es werden hier keine offiziellen Lösungen für die Übungsblätter gespeichert.
|
||||
|
||||
Die offiziellen Notizen zum Kurs sowie alle Übungsblätter, (Abgabe)termine, _etc._
|
||||
findet man ausschließlich auf der **Moodle**-Seite.
|
||||
|
||||
## (Wesentliche) Struktur des Repositorys ##
|
||||
|
||||
```text
|
||||
.
|
||||
├── ./notebooks # Python/Jupyter Notebooks
|
||||
│ ├── ...
|
||||
│ └── ... *.ipynb Dateien
|
||||
├── ./src # Quellcode
|
||||
│ ├── ...
|
||||
│ └── ... *.py Dateien
|
||||
├── ./templates # Templates für Konfiguration
|
||||
│ ├── ...
|
||||
│ └── ... *.yaml Dateien
|
||||
├── ./setup # User Konfiguration
|
||||
│ ├── ...
|
||||
│ └── ... *.yaml Dateien
|
||||
├── ./tests # enthält ggf. automatisiertes Testing
|
||||
├── ./dist # enthält VERSION infos
|
||||
├── ./models # erzeugte Modelle
|
||||
├── ./docs # Dokumentation für erzeugte Modelle
|
||||
│
|
||||
├── justfile
|
||||
├── README.md
|
||||
├── LICENSE
|
||||
├── requirements.txt
|
||||
└── main.py
|
||||
```
|
||||
|
||||
## Gebrauchshinweise ##
|
||||
|
||||
### Systemvoraussetzungen ###
|
||||
|
||||
- [**python 3.10.x**](https://www.python.org/downloads)
|
||||
- optional: **bash** (für Windowsuser siehe [git for windows](https://gitforwindows.org), was bash mit installiert)
|
||||
- optional: das [**justfile** Tool](https://github.com/casey/just#installation) (für Windowsuser wird die das [Chocolatey](https://chocolatey.org/install) Tool empfohlen, um dieses Tool installieren zu können)
|
||||
|
||||
### Setup ###
|
||||
|
||||
Für den Gebrauch der Python-Skripte + Notebooks braucht man einige Packages.
|
||||
Um diese zu installiere führe man
|
||||
```bash
|
||||
just build
|
||||
# oder
|
||||
just build-requirements
|
||||
```
|
||||
aus. Alternativ kann man
|
||||
```bash
|
||||
python3 -m pip install -r --disable-pip-version-check -r requirements.txt
|
||||
# für Windowsuser:
|
||||
py -3 -m pip install -r --disable-pip-version-check -r requirements.txt
|
||||
```
|
||||
verwenden.
|
||||
|
||||
### Ausführung ###
|
||||
|
||||
Die Notebooks kann man direkt in dem [**jupyter**](https://jupyter.org) GUI öffnen,
|
||||
oder in einem bash Terminal führe man
|
||||
```bash
|
||||
just notebook xyz
|
||||
```
|
||||
aus, um den Notebook **notebooks/xyz.ipynb** zu starten.
|
||||
Alternativ, kann man
|
||||
``` bash
|
||||
python3 -m jupyter notebook notebooks/xyz.ipynb
|
||||
# für Windowsuser:
|
||||
py -3 -m jupyter notebook notebooks/xyz.ipynb
|
||||
```
|
||||
verwenden.
|
1
dist/VERSION
vendored
Normal file
1
dist/VERSION
vendored
Normal file
@ -0,0 +1 @@
|
||||
0.0.0
|
259
justfile
Normal file
259
justfile
Normal file
@ -0,0 +1,259 @@
|
||||
# set shell := [ "bash", "-uc" ]
|
||||
_default:
|
||||
@- just --unsorted --list
|
||||
menu:
|
||||
@- just --unsorted --choose
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Justfile
|
||||
# NOTE: Do not change the contents of this file!
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# VARIABLES
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
OS := if os_family() == "windows" { "windows" } else { "linux" }
|
||||
PYTHON := if os_family() == "windows" { "py -3" } else { "python3" }
|
||||
GEN_MODELS := "datamodel-codegen"
|
||||
GEN_MODELS_DOCUMENTATION := "openapi-generator"
|
||||
PORT := "8000"
|
||||
URL_API := "http://127.0.0.1:8000/docs"
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Macros
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
_create-file-if-not-exists fname:
|
||||
@touch "{{fname}}";
|
||||
|
||||
_create-folder-if-not-exists path:
|
||||
@if ! [[ -d "{{path}}" ]]; then mkdir -p "{{path}}"; fi
|
||||
|
||||
_delete-if-file-exists fname:
|
||||
@if [[ -f "{{fname}}" ]]; then rm "{{fname}}"; fi
|
||||
|
||||
_delete-if-folder-exists path:
|
||||
@if [[ -d "{{path}}" ]]; then rm -rf "{{path}}"; fi
|
||||
|
||||
_clean-all-files path pattern:
|
||||
@find {{path}} -type f -name "{{pattern}}" -exec basename {} \; 2> /dev/null
|
||||
@- find {{path}} -type f -name "{{pattern}}" -exec rm {} \; 2> /dev/null
|
||||
|
||||
_clean-all-folders path pattern:
|
||||
@find {{path}} -type d -name "{{pattern}}" -exec basename {} \; 2> /dev/null
|
||||
@- find {{path}} -type d -name "{{pattern}}" -exec rm -rf {} \; 2> /dev/null
|
||||
|
||||
_copy-file-if-not-exists path_from path_to:
|
||||
@- cp -n "{{path_from}}" "{{path_to}}"
|
||||
|
||||
_check-python-tool tool name:
|
||||
#!/usr/bin/env bash
|
||||
success=false
|
||||
{{tool}} --help >> /dev/null 2> /dev/null && success=true;
|
||||
# NOTE: if exitcode is 251 (= help or print version), then render success.
|
||||
[[ "$?" == "251" ]] && success=true;
|
||||
# FAIL tool not installed
|
||||
if ( $success ); then
|
||||
echo -e "Tool \x1b[2;3m{{tool}}\x1b[0m installed correctly.";
|
||||
exit 0;
|
||||
else
|
||||
echo -e "Tool \x1b[2;3m{{tool}}\x1b[0m did not work." >> /dev/stderr;
|
||||
echo -e "Ensure that \x1b[2;3m{{name}}\x1b[0m (-> \x1b[1mjust build\x1b[0m) installed correctly and system paths are set." >> /dev/stderr;
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
_generate-models path name:
|
||||
@{{GEN_MODELS}} \
|
||||
--input-file-type openapi \
|
||||
--encoding "UTF-8" \
|
||||
--disable-timestamp \
|
||||
--use-schema-description \
|
||||
--allow-population-by-field-name \
|
||||
--snake-case-field \
|
||||
--strict-nullable \
|
||||
--target-python-version 3.10 \
|
||||
--input {{path}}/{{name}}-schema.yaml \
|
||||
--output {{path}}/generated/{{name}}.py
|
||||
|
||||
_generate-models-documentation path_schema path_docs name:
|
||||
@{{GEN_MODELS_DOCUMENTATION}} generate \
|
||||
--input-spec {{path_schema}}/{{name}}-schema.yaml \
|
||||
--generator-name markdown \
|
||||
--output "{{path_docs}}/{{name}}"
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS: build
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
build:
|
||||
@just build-misc
|
||||
@just build-requirements
|
||||
@just _check-system-requirements
|
||||
@just build-models
|
||||
build-misc:
|
||||
@# create database if not exists:
|
||||
@just _create-folder-if-not-exists "data"
|
||||
@# copy template of configs to setup if not exists:
|
||||
@just _create-folder-if-not-exists "setup"
|
||||
@just _copy-file-if-not-exists "templates/template-config.yaml" "setup/config.yaml"
|
||||
@just _copy-file-if-not-exists "templates/template.env" ".env"
|
||||
build-requirements:
|
||||
@{{PYTHON}} -m pip install --disable-pip-version-check -r requirements.txt
|
||||
build-models:
|
||||
@echo "Generate data models from schemata."
|
||||
@just _delete-if-folder-exists "models/generated"
|
||||
@just _create-folder-if-not-exists "models/generated"
|
||||
@- #just _generate-models "models" "config"
|
||||
build-documentation:
|
||||
@echo "Generate documentations data models from schemata."
|
||||
@just _delete-if-folder-exists "docs"
|
||||
@just _create-folder-if-not-exists "docs"
|
||||
@- #just _generate-models-documentation "models" "docs" "config"
|
||||
@- just _clean-all-files "." .openapi-generator*
|
||||
@- just _clean-all-folders "." .openapi-generator*
|
||||
dist:
|
||||
@just build
|
||||
@just build-documentation
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS: run
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# runs using commandline interface
|
||||
run:
|
||||
@# create config/data folders if missing:
|
||||
@just build-misc
|
||||
@# run source code als cli
|
||||
@{{PYTHON}} main.py
|
||||
|
||||
# runs python notebook (in browser)
|
||||
notebook name="main":
|
||||
@# create config/data folders if missing:
|
||||
@just build-misc
|
||||
@# run notebook
|
||||
@{{PYTHON}} -m jupyter notebook notebooks/{{name}}.ipynb
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS: tests
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
tests: tests-unit tests-integration
|
||||
tests-logs:
|
||||
@just _create-logs
|
||||
@- just tests
|
||||
@just _display-logs
|
||||
tests-unit-logs:
|
||||
@just _create-logs
|
||||
@- just tests-unit
|
||||
@just _display-logs
|
||||
tests-integration-logs:
|
||||
@just _create-logs
|
||||
@- just tests-integration
|
||||
@just _display-logs
|
||||
tests-unit:
|
||||
@{{PYTHON}} -m pytest tests \
|
||||
--ignore=tests/integration \
|
||||
--cov-reset \
|
||||
--cov=. \
|
||||
2> /dev/null
|
||||
tests-integration:
|
||||
@{{PYTHON}} -m pytest tests/integration 2> /dev/null
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS: qa
|
||||
# NOTE: use for development only.
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
qa:
|
||||
@{{PYTHON}} -m coverage report -m
|
||||
coverage source_path tests_path:
|
||||
@just _create-logs
|
||||
@-just _coverage-no-logs "{{source_path}}" "{{tests_path}}"
|
||||
@just _display-logs
|
||||
_coverage-no-logs source_path tests_path:
|
||||
@{{PYTHON}} -m pytest {{tests_path}} \
|
||||
--ignore=tests/integration \
|
||||
--cov-reset \
|
||||
--cov={{source_path}} \
|
||||
--capture=tee-sys \
|
||||
2> /dev/null
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS: clean
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
clean:
|
||||
@just clean-basic
|
||||
@just pre-commit
|
||||
pre-commit:
|
||||
@echo "Clean python notebooks."
|
||||
@{{PYTHON}} -m jupyter nbconvert --clear-output --inplace **/*.ipynb
|
||||
@- {{PYTHON}} -m jupytext --update-metadata '{"vscode":""}' **/*.ipynb 2> /dev/null
|
||||
@- {{PYTHON}} -m jupytext --update-metadata '{"vscode":null}' **/*.ipynb 2> /dev/null
|
||||
clean-basic:
|
||||
@echo "All system artefacts will be force removed."
|
||||
@- just _clean-all-files "." ".DS_Store" 2> /dev/null
|
||||
@echo "All build artefacts will be force removed."
|
||||
@- just _clean-all-folders "." "__pycache__" 2> /dev/null
|
||||
@- just _delete-if-folder-exists "models/generated" 2> /dev/null
|
||||
@echo "All test artefacts will be force removed."
|
||||
@- just _clean-all-folders "." ".pytest_cache" 2> /dev/null
|
||||
@- just _delete-if-file-exists ".coverage" 2> /dev/null
|
||||
@- just _delete-if-folder-exists "logs" 2> /dev/null
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS: logging
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
_create-logs:
|
||||
@# For logging purposes (since stdout is rechanneled):
|
||||
@just _delete-if-file-exists "logs/debug.log"
|
||||
@just _create-folder-if-not-exists "logs"
|
||||
@just _create-file-if-not-exists "logs/debug.log"
|
||||
_display-logs:
|
||||
@echo ""
|
||||
@echo "Content of logs/debug.log:"
|
||||
@echo "----------------"
|
||||
@echo ""
|
||||
@- cat logs/debug.log
|
||||
@echo ""
|
||||
@echo "----------------"
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS: processes
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
api-process:
|
||||
#!/usr/bin/env bash
|
||||
if [[ "{{OS}}" == "linux" ]]; then
|
||||
ps aux | grep "uvicorn src.api:app"
|
||||
else
|
||||
netstat -ano | findstr "uvicorn src.api:app"
|
||||
fi
|
||||
kill-api-process:
|
||||
#!/usr/bin/env bash
|
||||
# NOTE: only need to do this for linux.
|
||||
if [[ "{{OS}}" == "linux" ]]; then
|
||||
echo "Terminating all processes associated to app and port {{PORT}}."
|
||||
while read pid; do
|
||||
if [[ "$pid" == "" ]]; then continue; fi
|
||||
echo "- killing process $pid:"
|
||||
kill -9 ${pid};
|
||||
done <<< $( pgrep -f "uvicorn src.api:app --port {{PORT}}" )
|
||||
fi
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# TARGETS: requirements
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
_check-system:
|
||||
@echo "Operating System detected: {{os_family()}}."
|
||||
@echo "Python command used: {{PYTHON}}."
|
||||
|
||||
_check-system-requirements:
|
||||
@just _check-python-tool "{{GEN_MODELS}}" "datamodel-code-generator"
|
||||
@just _check-python-tool "{{GEN_MODELS_DOCUMENTATION}}" "openapi-code-generator"
|
28
main.py
Normal file
28
main.py
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
import os;
|
||||
import sys;
|
||||
|
||||
os.chdir(os.path.dirname(__file__));
|
||||
sys.path.insert(0, os.getcwd());
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# MAIN
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def enter():
|
||||
print('Not yet implemented.');
|
||||
return;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXECUTION
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
if __name__ == '__main__':
|
||||
# sys.tracebacklimit = 0;
|
||||
enter();
|
57
pyproject.toml
Normal file
57
pyproject.toml
Normal file
@ -0,0 +1,57 @@
|
||||
[project]
|
||||
name = "course-ads-1"
|
||||
version = "X.Y.Z"
|
||||
description = ""
|
||||
authors = [ "gitea.math.uni-leipzig.de/raj_mathe" ]
|
||||
maintainers = [ "gitea.math.uni-leipzig.de/raj_mathe" ]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
python = "^3.10"
|
||||
homepage = "https://gitea.math.uni-leipzig.de/raj_mathe/mfp1-2022"
|
||||
repository = "https://gitea.math.uni-leipzig.de/raj_mathe/mfp1-2022"
|
||||
documentation = "https://gitea.math.uni-leipzig.de/raj_mathe/mfp1-2022/src/README.md"
|
||||
keywords = [
|
||||
"python",
|
||||
]
|
||||
# cf. https://pypi.org/classifiers
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: Unix",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "7.1.1"
|
||||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
python_files = [
|
||||
"**/tests_*.py",
|
||||
]
|
||||
asyncio_mode = "auto"
|
||||
filterwarnings = [
|
||||
"error",
|
||||
"ignore::UserWarning",
|
||||
"ignore::DeprecationWarning",
|
||||
]
|
||||
# NOTE: appends (not prepends) flags:
|
||||
addopts = [
|
||||
"--order-dependencies",
|
||||
"--order-group-scope=module",
|
||||
"--cache-clear",
|
||||
"--verbose",
|
||||
"--maxfail=1",
|
||||
"-k test_",
|
||||
# NOTE: will be ignored, if --cov not used (e.g. integration tests):
|
||||
"--no-cov-on-fail",
|
||||
"--cov-report=term",
|
||||
"--cov-config=.coveragerc",
|
||||
# NOTE: for development purposes only:
|
||||
# "-s", # verbose print/err capturing disabled
|
||||
# "--capture=tee-sys", # verbose print/err capturing enabled
|
||||
]
|
49
requirements.txt
Normal file
49
requirements.txt
Normal file
@ -0,0 +1,49 @@
|
||||
pip>=22.2.2
|
||||
wheel>=0.37.1
|
||||
|
||||
# jupyter
|
||||
ipython>=8.3.0
|
||||
jupyter>=1.0.0
|
||||
|
||||
# running
|
||||
codetiming>=1.3.0
|
||||
|
||||
# testing + dev
|
||||
coverage[toml]>=6.4
|
||||
pytest>=7.1.1
|
||||
pytest-asyncio>=0.18.3
|
||||
pytest-cov>=3.0.0
|
||||
pytest-lazy-fixture>=0.6.3
|
||||
pytest-order>=1.0.1
|
||||
testfixtures>=6.18.5
|
||||
|
||||
# misc
|
||||
importlib>=1.0.4
|
||||
authlib>=1.0.1
|
||||
passlib>=1.7.4
|
||||
psutil>=5.9.0
|
||||
pathlib>=1.0.1
|
||||
lorem>=0.1.1
|
||||
safetywrap>=1.5.0
|
||||
typing>=3.7.4.3
|
||||
nptyping>=2.1.1
|
||||
typing-extensions>=3.10.0.2
|
||||
|
||||
# config
|
||||
python-dotenv>=0.20.0
|
||||
jsonschema>=4.4.0
|
||||
lazy-load>=0.8.2
|
||||
pyyaml>=5.4.1
|
||||
pydantic>=1.9.1
|
||||
datamodel-code-generator>=0.12.0
|
||||
|
||||
# maths
|
||||
fraction>=2.2.0
|
||||
numpy>=1.22.4
|
||||
matplotlib>=3.5.1
|
||||
|
||||
# tables, data frames
|
||||
pandas>=1.4.2
|
||||
tabulate>=0.8.10
|
||||
# array-to-latex>=0.82 # <- has issues
|
||||
qiskit[visualization]>=0.38.0
|
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
0
src/core/__init__.py
Normal file
0
src/core/__init__.py
Normal file
215
src/core/calls.py
Normal file
215
src/core/calls.py
Normal file
@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from __future__ import annotations;
|
||||
|
||||
from src.thirdparty.code import *;
|
||||
from src.thirdparty.config import *;
|
||||
from src.thirdparty.types import *;
|
||||
|
||||
from src.core.utils import *;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'CallState',
|
||||
'GetState',
|
||||
'CallValue',
|
||||
'CallError',
|
||||
'keep_calm_and_carry_on',
|
||||
'run_safely',
|
||||
'to_async',
|
||||
];
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# CONSTANTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# local usage only
|
||||
T = TypeVar('T');
|
||||
V = TypeVar('V');
|
||||
ARGS = ParamSpec('ARGS');
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Class State for keeping track of results in the course of a computation
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@dataclass
|
||||
class CallState(Generic[V]):
|
||||
'''
|
||||
An auxiliary class which keeps track of the latest state of during calls.
|
||||
'''
|
||||
tag: Option[str] = field(default=None);
|
||||
result: Optional[V] = field(default=None, repr=False);
|
||||
timestamp: str = field(default_factory=get_timestamp_string);
|
||||
has_action: bool = field(default=False);
|
||||
no_action: bool = field(default=False);
|
||||
has_error: bool = field(default=False);
|
||||
values: list[tuple[bool, dict]] = field(default_factory=list, repr=False);
|
||||
errors: list[str] = field(default_factory=list, repr=False);
|
||||
|
||||
def __copy__(self) -> CallState:
|
||||
return CallState(**asdict(self));
|
||||
|
||||
def __add__(self, other) -> CallState:
|
||||
'''
|
||||
Combines states sequentially:
|
||||
- takes on latest `timestamp`.
|
||||
- takes on lates `value`.
|
||||
- takes on `tag` of latest non-null value.
|
||||
- takes on latest value of `no_action`
|
||||
- `has_action` = `true` <==> at least one action taken,
|
||||
unless `no_action` = `true`.
|
||||
- `has_error` = `true` <==> at least one error occurred.
|
||||
'''
|
||||
if isinstance(other, CallState):
|
||||
no_action = other.no_action;
|
||||
has_action = False if no_action else (self.has_action or other.has_action);
|
||||
return CallState(
|
||||
tag = other.tag or self.tag,
|
||||
value = other.value,
|
||||
timestamp = other.timestamp,
|
||||
has_action = has_action,
|
||||
no_action = no_action,
|
||||
has_error = self.has_error or other.has_error,
|
||||
values = self.values + other.values,
|
||||
errors = self.errors + other.errors,
|
||||
);
|
||||
raise Exception('Cannot add states!');
|
||||
|
||||
## NOTE: only need __radd__ for additions of form <other> + <state>
|
||||
def __radd__(self, other) -> CallState:
|
||||
if other == 0:
|
||||
return self.__copy__();
|
||||
raise Exception('Cannot add a CallState to the right of a non-zero object!');
|
||||
|
||||
def get_result(self) -> V:
|
||||
if self.result is not None:
|
||||
return self.result;
|
||||
raise Exception('No result set!');
|
||||
|
||||
@property
|
||||
def first_data(self) -> dict:
|
||||
'''
|
||||
Returns data in first value collected or else defaults to empty dictionary.
|
||||
'''
|
||||
return self.values[0][1] if len(self.values) > 0 else dict();
|
||||
|
||||
@property
|
||||
def data(self) -> list[dict]:
|
||||
'''
|
||||
Returns the data collected.
|
||||
'''
|
||||
return [ data for _, data in self.values ];
|
||||
|
||||
@property
|
||||
def data_log(self) -> list[dict]:
|
||||
'''
|
||||
Returns the data to be logged.
|
||||
'''
|
||||
return [ data for log, data in self.values if log == True ];
|
||||
|
||||
@property
|
||||
def data_log_json(self) -> list[str]:
|
||||
'''
|
||||
Returns the data to be logged as json.
|
||||
'''
|
||||
return list(map(json.dumps, self.data_log));
|
||||
|
||||
def GetState(result: Result[CallState, CallState]) -> CallState:
|
||||
if isinstance(result, Ok):
|
||||
return result.unwrap();
|
||||
return result.unwrap_err();
|
||||
|
||||
def CallValue(
|
||||
tag: str = None,
|
||||
result: Optional[V] = None,
|
||||
has_action: bool = True,
|
||||
no_action: bool = False,
|
||||
value: Option[tuple[bool, dict] | list[tuple[bool, dict]]] = Nothing(),
|
||||
) -> CallState[V]:
|
||||
x = [];
|
||||
if isinstance(value, Some):
|
||||
x = value.unwrap() or [];
|
||||
x = x if isinstance(x, list) else [ x ];
|
||||
X = CallState(tag=tag, result=result, values=x, has_action=has_action, no_action=no_action, has_error=False);
|
||||
return X;
|
||||
|
||||
def CallError(
|
||||
tag: str = None,
|
||||
has_action: bool = True,
|
||||
error: Option[str | BaseException | list[str | BaseException]] = Nothing(),
|
||||
) -> CallState[V]:
|
||||
x = [];
|
||||
if isinstance(error, Some):
|
||||
x = error.unwrap() or [];
|
||||
x = x if isinstance(x, list) else [ x ];
|
||||
x = list(map(str, x));
|
||||
return CallState(tag=tag, errors=x, has_action=has_action, has_error=True);
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# DECORATOR - forces methods to run safely
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def run_safely(tag: str | None = None, error_message: str | None = None):
|
||||
'''
|
||||
Creates a decorator for an action to perform it safely.
|
||||
|
||||
@inputs (parameters)
|
||||
- `tag` - optional string to aid error tracking.
|
||||
- `error_message` - optional string for an error message.
|
||||
|
||||
### Example usage ###
|
||||
```py
|
||||
@run_safely(tag='recognise int', error_message='unrecognise string')
|
||||
def action1(x: str) -> Result[int, CallState]:
|
||||
return Ok(int(x));
|
||||
|
||||
assert action1('5') == Ok(5);
|
||||
result = action1('not a number');
|
||||
assert isinstance(result, Err);
|
||||
err = result.unwrap_err();
|
||||
assert isinstance(err, CallState);
|
||||
assert err.tag == 'recognise int';
|
||||
assert err.errors == ['unrecognise string'];
|
||||
|
||||
@run_safely('recognise int')
|
||||
def action2(x: str) -> Result[int, CallState]:
|
||||
return Ok(int(x));
|
||||
|
||||
assert action2('5') == Ok(5);
|
||||
result = action2('not a number');
|
||||
assert isinstance(result, Err);
|
||||
err = result.unwrap_err();
|
||||
assert isinstance(err, CallState);
|
||||
assert err.tag == 'recognise int';
|
||||
assert len(err.errors) == 1;
|
||||
```
|
||||
NOTE: in the second example, err.errors is a list containing
|
||||
the stringified Exception generated when calling `int('not a number')`.
|
||||
'''
|
||||
def dec(action: Callable[ARGS, Result[V, CallState]]) -> Callable[ARGS, Result[V, CallState]]:
|
||||
'''
|
||||
Wraps action with return type Result[..., CallState],
|
||||
so that it is performed safely a promise,
|
||||
catching any internal exceptions as an Err(...)-component of the Result.
|
||||
'''
|
||||
@wraps(action)
|
||||
def wrapped_action(*_, **__) -> Result[V, CallState]:
|
||||
# NOTE: intercept Exceptions first, then flatten:
|
||||
return Result.of(lambda: action(*_, **__)) \
|
||||
.or_else(
|
||||
lambda err: Err(CallError(
|
||||
tag = tag or action.__name__,
|
||||
error = Some(error_message or err),
|
||||
))
|
||||
) \
|
||||
.flatmap(lambda value: value); # necessary to flatten result.
|
||||
return wrapped_action;
|
||||
return dec;
|
61
src/core/env.py
Normal file
61
src/core/env.py
Normal file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from src.thirdparty.code import *;
|
||||
from src.thirdparty.config import *;
|
||||
from src.thirdparty.system import *;
|
||||
from src.thirdparty.types import *;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'get_env_string',
|
||||
'get_env_optional_string',
|
||||
'get_env_int',
|
||||
'get_env_optional_int',
|
||||
'get_env_float',
|
||||
'get_env_optional_float',
|
||||
];
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# AUXILIARY METHODS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def get_env_value(env: dict, key: str, default: Any = None) -> Any: # pragma: no cover
|
||||
return env[key] if key in env else default;
|
||||
|
||||
def get_env_string(env: dict, key: str, default: Optional[str] = None) -> str:
|
||||
result = Result.of(lambda: str(env[key] or default));
|
||||
if default is None:
|
||||
return result.unwrap();
|
||||
return result.unwrap_or(default);
|
||||
|
||||
def get_env_optional_string(env: dict, key: str) -> Optional[str]:
|
||||
result = Result.of(lambda: str(env[key]));
|
||||
return result.unwrap_or(None);
|
||||
|
||||
def get_env_int(env: dict, key: str, default: Optional[int] = None) -> int:
|
||||
result = Result.of(lambda: int(env[key] or default));
|
||||
if default is None:
|
||||
return result.unwrap();
|
||||
return result.unwrap_or(default);
|
||||
|
||||
def get_env_optional_int(env: dict, key: str) -> Optional[int]:
|
||||
result = Result.of(lambda: int(env[key]));
|
||||
return result.unwrap_or(None);
|
||||
|
||||
def get_env_float(env: dict, key: str, default: Optional[float] = None) -> float:
|
||||
result = Result.of(lambda: float(env[key] or default));
|
||||
if default is None:
|
||||
return result.unwrap();
|
||||
return result.unwrap_or(default);
|
||||
|
||||
def get_env_optional_float(env: dict, key: str) -> Optional[float]:
|
||||
result = Result.of(lambda: float(env[key]));
|
||||
return result.unwrap_or(None);
|
163
src/core/log.py
Normal file
163
src/core/log.py
Normal file
@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from src.thirdparty.code import *;
|
||||
from src.thirdparty.io import *;
|
||||
from src.thirdparty.log import *;
|
||||
from src.thirdparty.misc import *;
|
||||
from src.thirdparty.types import *;
|
||||
|
||||
from src.core.calls import *;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'LOG_LEVELS',
|
||||
'configure_logging',
|
||||
'log_info',
|
||||
'log_debug',
|
||||
'log_warn',
|
||||
'log_error',
|
||||
'log_fatal',
|
||||
'log_result',
|
||||
'log_dev',
|
||||
'catch_fatal',
|
||||
'prompt_user_input',
|
||||
'prompt_secure_user_input',
|
||||
'prompt_confirmation',
|
||||
];
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# CONSTANTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
class LOG_LEVELS(Enum): # pragma: no cover
|
||||
INFO = logging.INFO;
|
||||
DEBUG = logging.DEBUG;
|
||||
|
||||
# local usage only
|
||||
_LOGGING_DEBUG_FILE: str = 'logs/debug.log';
|
||||
T = TypeVar('T');
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHODS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def configure_logging(level: LOG_LEVELS): # pragma: no cover
|
||||
logging.basicConfig(
|
||||
format = '%(asctime)s [\x1b[1m%(levelname)s\x1b[0m] %(message)s',
|
||||
level = level.value,
|
||||
datefmt = r'%Y-%m-%d %H:%M:%S',
|
||||
);
|
||||
return;
|
||||
|
||||
def log_debug(*messages: Any):
|
||||
logging.debug(*messages);
|
||||
|
||||
def log_info(*messages: Any):
|
||||
logging.info(*messages);
|
||||
|
||||
def log_warn(*messages: Any):
|
||||
logging.warning(*messages);
|
||||
|
||||
def log_error(*messages: Any):
|
||||
logging.error(*messages);
|
||||
|
||||
def log_fatal(*messages: Any):
|
||||
logging.fatal(*messages);
|
||||
exit(1);
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Special Methods
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# Intercept fatal errors
|
||||
def catch_fatal(method: Callable[[], T]) -> T:
|
||||
try:
|
||||
return method();
|
||||
except Exception as e:
|
||||
log_fatal(e);
|
||||
|
||||
def log_result(result: Result[CallState, CallState], debug: bool = False):
|
||||
'''
|
||||
Logs safely encapsulated result of call as either debug/info or error.
|
||||
|
||||
@inputs
|
||||
- `result` - the result of the call.
|
||||
- `debug = False` (default) - if the result is okay, will be logged as an INFO message.
|
||||
- `debug = True` - if the result is okay, will be logged as a DEBUG message.
|
||||
'''
|
||||
if isinstance(result, Ok):
|
||||
value = result.unwrap();
|
||||
log_debug(value);
|
||||
for x in value.data_log:
|
||||
log_info(x);
|
||||
else:
|
||||
err = result.unwrap_err();
|
||||
log_debug(err);
|
||||
for e in err.errors:
|
||||
log_error(e);
|
||||
return;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# DEBUG LOGGING FOR DEVELOPMENT
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def log_dev(*messages: Any): # pragma: no cover
|
||||
with open(_LOGGING_DEBUG_FILE, 'a') as fp:
|
||||
print(*messages, file=fp);
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# User Input
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def prompt_user_input(message: str, expectedformat: Callable) -> Optional[str]:
|
||||
answer = None;
|
||||
while True:
|
||||
try:
|
||||
answer = input(f'{message}: ');
|
||||
## Capture Meta+C:
|
||||
except KeyboardInterrupt:
|
||||
print('');
|
||||
return None;
|
||||
## Capture Meta+D:
|
||||
except EOFError:
|
||||
print('');
|
||||
return None;
|
||||
except:
|
||||
continue;
|
||||
if expectedformat(answer):
|
||||
break;
|
||||
return answer;
|
||||
|
||||
def prompt_secure_user_input(message: str, expectedformat: Callable) -> Optional[str]:
|
||||
answer = None;
|
||||
while True:
|
||||
try:
|
||||
## TODO: zeige **** ohne Zeilenumbruch an.
|
||||
answer = getpass(f'{message}: ', stream=None);
|
||||
## Capture Meta+C:
|
||||
except KeyboardInterrupt:
|
||||
print('');
|
||||
return None;
|
||||
## Capture Meta+D:
|
||||
except EOFError:
|
||||
print('');
|
||||
return None;
|
||||
except:
|
||||
continue;
|
||||
if expectedformat(answer):
|
||||
break;
|
||||
return answer;
|
||||
|
||||
def prompt_confirmation(message: str, default: bool = False) -> bool:
|
||||
answer = prompt_user_input(message, lambda x: not not re.match(r'^(y|yes|j|ja|n|no|nein)$', x));
|
||||
if isinstance(answer, str):
|
||||
return True if re.match(r'^(y|yes|j|ja)$', answer) else False;
|
||||
return default;
|
36
src/core/utils.py
Normal file
36
src/core/utils.py
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from src.thirdparty.code import *;
|
||||
from src.thirdparty.misc import *;
|
||||
from src.thirdparty.types import *;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'flatten',
|
||||
'get_timestamp_string',
|
||||
];
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHODS - date, time
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def get_timestamp() -> datetime: # pragma: no cover
|
||||
return datetime.now();
|
||||
|
||||
def get_timestamp_string() -> str: # pragma: no cover
|
||||
return str(get_timestamp());
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHODS arrays
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def flatten(X: list[list[Any]]) -> list[Any]:
|
||||
return list(itertools_chain.from_iterable(X));
|
0
src/thirdparty/__init__.py
vendored
Normal file
0
src/thirdparty/__init__.py
vendored
Normal file
51
src/thirdparty/code.py
vendored
Normal file
51
src/thirdparty/code.py
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from functools import partial;
|
||||
from functools import reduce;
|
||||
from functools import wraps;
|
||||
from dataclasses import Field;
|
||||
from dataclasses import MISSING;
|
||||
from dataclasses import asdict;
|
||||
from dataclasses import dataclass;
|
||||
from dataclasses import field;
|
||||
from itertools import chain as itertools_chain;
|
||||
from itertools import product as itertools_product;
|
||||
from operator import itemgetter;
|
||||
from pydantic import BaseModel;
|
||||
# cf. https://github.com/mplanchard/safetywrap
|
||||
from safetywrap import Ok;
|
||||
from safetywrap import Err;
|
||||
from safetywrap import Nothing;
|
||||
from safetywrap import Result;
|
||||
from safetywrap import Option;
|
||||
from safetywrap import Some;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'partial',
|
||||
'reduce',
|
||||
'wraps',
|
||||
'asdict',
|
||||
'dataclass',
|
||||
'field',
|
||||
'Field',
|
||||
'MISSING',
|
||||
'itertools_chain',
|
||||
'itertools_product',
|
||||
'itemgetter',
|
||||
'BaseModel',
|
||||
'Err',
|
||||
'Nothing',
|
||||
'Ok',
|
||||
'Option',
|
||||
'Result',
|
||||
'Some',
|
||||
];
|
32
src/thirdparty/config.py
vendored
Normal file
32
src/thirdparty/config.py
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from dotenv import load_dotenv;
|
||||
from dotenv import dotenv_values;
|
||||
import json;
|
||||
import jsonschema;
|
||||
from lazy_load import lazy;
|
||||
from yaml import add_constructor;
|
||||
from yaml import load as yaml_load;
|
||||
from yaml import FullLoader as yaml_FullLoader;
|
||||
from yaml import add_path_resolver as yaml_add_path_resolver;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'load_dotenv',
|
||||
'dotenv_values',
|
||||
'json',
|
||||
'jsonschema',
|
||||
'lazy',
|
||||
'add_constructor',
|
||||
'yaml_load',
|
||||
'yaml_FullLoader',
|
||||
'yaml_add_path_resolver',
|
||||
];
|
16
src/thirdparty/io.py
vendored
Normal file
16
src/thirdparty/io.py
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from getpass import getpass;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'getpass',
|
||||
];
|
18
src/thirdparty/log.py
vendored
Normal file
18
src/thirdparty/log.py
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
import logging;
|
||||
from logging import LogRecord;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'logging',
|
||||
'LogRecord',
|
||||
];
|
22
src/thirdparty/maths.py
vendored
Normal file
22
src/thirdparty/maths.py
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from fractions import Fraction;
|
||||
import math;
|
||||
import numpy as np;
|
||||
import random;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'Fraction',
|
||||
'math',
|
||||
'np',
|
||||
'random',
|
||||
];
|
24
src/thirdparty/misc.py
vendored
Normal file
24
src/thirdparty/misc.py
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from datetime import datetime;
|
||||
from datetime import timedelta;
|
||||
import lorem;
|
||||
import re;
|
||||
from textwrap import dedent;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'lorem',
|
||||
're',
|
||||
'datetime',
|
||||
'timedelta',
|
||||
'dedent',
|
||||
];
|
27
src/thirdparty/render.py
vendored
Normal file
27
src/thirdparty/render.py
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from IPython.display import Latex;
|
||||
from IPython.display import display_latex;
|
||||
from IPython.display import display_png;
|
||||
from IPython.display import display_markdown;
|
||||
from IPython.display import display;
|
||||
# from array_to_latex import to_ltx as array_to_latex; # <- has issues
|
||||
from qiskit.visualization import array_to_latex;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'Latex',
|
||||
'display',
|
||||
'display_latex',
|
||||
'display_png',
|
||||
'display_markdown',
|
||||
'array_to_latex',
|
||||
];
|
16
src/thirdparty/run.py
vendored
Normal file
16
src/thirdparty/run.py
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from codetiming import Timer;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'Timer',
|
||||
];
|
20
src/thirdparty/system.py
vendored
Normal file
20
src/thirdparty/system.py
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
import os;
|
||||
from pathlib import Path;
|
||||
import sys;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'os',
|
||||
'Path',
|
||||
'sys',
|
||||
];
|
84
src/thirdparty/types.py
vendored
Normal file
84
src/thirdparty/types.py
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from enum import Enum;
|
||||
from io import BytesIO;
|
||||
from nptyping import NDArray;
|
||||
from nptyping import Shape;
|
||||
from nptyping import Bool;
|
||||
from nptyping import UInt;
|
||||
from nptyping import UInt8;
|
||||
from nptyping import UInt32;
|
||||
from nptyping import UInt64;
|
||||
from nptyping import Int;
|
||||
from nptyping import Int32;
|
||||
from nptyping import Int64;
|
||||
from nptyping import Float;
|
||||
from nptyping import Float32;
|
||||
from nptyping import Float64;
|
||||
from numpy import uint8;
|
||||
from numpy import int32;
|
||||
from numpy import int64;
|
||||
from numpy import float32;
|
||||
from numpy import float64;
|
||||
from numpy import complex64;
|
||||
from numpy import complex128;
|
||||
from pydantic import conint;
|
||||
from typing import Any;
|
||||
from typing import Awaitable;
|
||||
from typing import Callable;
|
||||
from typing import ClassVar;
|
||||
from typing import Coroutine;
|
||||
from typing import Generator;
|
||||
from typing import Generic;
|
||||
from typing import Optional;
|
||||
from typing import Type;
|
||||
from typing import TypeVar;
|
||||
from typing_extensions import Concatenate;
|
||||
from typing_extensions import ParamSpec;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'Enum',
|
||||
'BytesIO',
|
||||
'NDArray',
|
||||
'Shape',
|
||||
'Bool',
|
||||
'UInt',
|
||||
'UInt8',
|
||||
'UInt32',
|
||||
'UInt64',
|
||||
'Int',
|
||||
'Int32',
|
||||
'Int64',
|
||||
'Float',
|
||||
'Float32',
|
||||
'Float64',
|
||||
'uint8',
|
||||
'int32',
|
||||
'int64',
|
||||
'float32',
|
||||
'float64',
|
||||
'complex64',
|
||||
'complex128',
|
||||
'conint',
|
||||
'Any',
|
||||
'Awaitable',
|
||||
'Callable',
|
||||
'ClassVar',
|
||||
'Coroutine',
|
||||
'Generator',
|
||||
'Generic',
|
||||
'Optional',
|
||||
'Type',
|
||||
'TypeVar',
|
||||
'Concatenate',
|
||||
'ParamSpec',
|
||||
];
|
0
templates/template-config.yaml
Normal file
0
templates/template-config.yaml
Normal file
0
templates/template.env
Normal file
0
templates/template.env
Normal file
1
tests/README.md
Normal file
1
tests/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Tests #
|
Loading…
Reference in New Issue
Block a user