Commit 63a54b5d authored by eckhart's avatar eckhart
Browse files

Merge branch 'development' of gitlab.lrz.de:badw-it/DHParser into development

parents 99e401cc d3009b08
...@@ -28,7 +28,6 @@ DevScripts/DHParser.py ...@@ -28,7 +28,6 @@ DevScripts/DHParser.py
DHParser/cstringview.c DHParser/cstringview.c
*.so *.so
.mypy_cache .mypy_cache
.vscode/
DHParser.egg-info DHParser.egg-info
.noseids/ .noseids/
*.build *.build
...@@ -42,7 +41,6 @@ _build ...@@ -42,7 +41,6 @@ _build
_build _build
_static _static
_templates _templates
.vs
OLDSTUFF OLDSTUFF
.pytest_cache .pytest_cache
*.c *.c
......
...@@ -102,7 +102,8 @@ class Compiler: ...@@ -102,7 +102,8 @@ class Compiler:
context: A list of parent nodes that ends with the currently context: A list of parent nodes that ends with the currently
compiled node. compiled node.
tree: The root of the abstract syntax tree. tree: The root of the abstract syntax tree.
source: The source code. finalizers: A stack of tuples (function, parameters) that will be
called in reverse order after compilation.
_dirty_flag: A flag indicating that the compiler has already been _dirty_flag: A flag indicating that the compiler has already been
called at least once and that therefore all compilation called at least once and that therefore all compilation
...@@ -120,12 +121,28 @@ class Compiler: ...@@ -120,12 +121,28 @@ class Compiler:
def reset(self): def reset(self):
# self.source = '' # self.source = ''
self.finlizers = [] # type: List[Callable, Tuple]
self.tree = ROOTNODE_PLACEHOLDER # type: RootNode self.tree = ROOTNODE_PLACEHOLDER # type: RootNode
self.context = [] # type: List[Node] self.context = [] # type: List[Node]
self._None_check = True # type: bool self._None_check = True # type: bool
self._dirty_flag = False self._dirty_flag = False
self._debug = get_config_value('debug') # type: bool self._debug = get_config_value('debug') # type: bool
self._debug_already_compiled = set() # type: Set[Node] self._debug_already_compiled = set() # type: Set[Node]
self.finalizers = [] # type: List[Callable, Tuple]
def prepare(self) -> None:
"""
A preparation method that will be called after everything else has
been initialized and immediately before compilation starts. This method
can be overwritten in order to implement preparation tasks.
"""
pass
def finalize(self) -> None:
"""
A finalization method that is called after compilation has finished and
after all tasks from the finalizers stack have been executed
"""
def __call__(self, root: RootNode) -> Any: def __call__(self, root: RootNode) -> Any:
""" """
...@@ -141,7 +158,11 @@ class Compiler: ...@@ -141,7 +158,11 @@ class Compiler:
self._dirty_flag = True self._dirty_flag = True
self.tree = root # type: RootNode self.tree = root # type: RootNode
# self.source = source # type: str # self.source = source # type: str
self.prepare()
result = self.compile(root) result = self.compile(root)
while self.finalizers:
task, parameters = self.finalizers.pop()
task(*parameters)
return result return result
# Obsolete, because never used... # Obsolete, because never used...
......
...@@ -86,6 +86,7 @@ class Error: ...@@ -86,6 +86,7 @@ class Error:
PARSER_DID_NOT_MATCH = ErrorCode(1020) PARSER_DID_NOT_MATCH = ErrorCode(1020)
PARSER_LOOKAHEAD_MATCH_ONLY = ErrorCode(1030) PARSER_LOOKAHEAD_MATCH_ONLY = ErrorCode(1030)
PARSER_STOPPED_BEFORE_END = ErrorCode(1040) PARSER_STOPPED_BEFORE_END = ErrorCode(1040)
PARSER_STOPPED_EXCEPT_FOR_LOOKAHEAD = ErrorCode(1045)
CAPTURE_STACK_NOT_EMPTY = ErrorCode(1050) CAPTURE_STACK_NOT_EMPTY = ErrorCode(1050)
MALFORMED_ERROR_STRING = ErrorCode(1060) MALFORMED_ERROR_STRING = ErrorCode(1060)
AMBIGUOUS_ERROR_HANDLING = ErrorCode(1070) AMBIGUOUS_ERROR_HANDLING = ErrorCode(1070)
......
...@@ -974,7 +974,9 @@ class Grammar: ...@@ -974,7 +974,9 @@ class Grammar:
result, 'Parser "%s" did not match empty document.' % str(parser), result, 'Parser "%s" did not match empty document.' % str(parser),
Error.PARSER_DID_NOT_MATCH) Error.PARSER_DID_NOT_MATCH)
while rest and len(stitches) < self.max_parser_dropouts__: # copy to local variable, so break condition can be triggered manually
max_parser_dropouts = self.max_parser_dropouts__
while rest and len(stitches) < max_parser_dropouts:
result, rest = parser(rest) result, rest = parser(rest)
if rest: if rest:
fwd = rest.find("\n") + 1 or len(rest) fwd = rest.find("\n") + 1 or len(rest)
...@@ -994,13 +996,21 @@ class Grammar: ...@@ -994,13 +996,21 @@ class Grammar:
error_code = Error.PARSER_DID_NOT_MATCH error_code = Error.PARSER_DID_NOT_MATCH
else: else:
stitches.append(result) stitches.append(result)
error_msg = "Parser stopped before end" \ h = self.history__[-1] if self.history__ else \
+ (("! trying to recover" HistoryRecord([], None, StringView(''), (0, 0))
+ (" but stopping history recording at this point." if h.status == h.MATCH and (h.node.pos + len(h.node) == len(self.document__)):
if self.history_tracking__ else "...")) # TODO: this case still needs unit-tests and support in testing.py
if len(stitches) < self.max_parser_dropouts__ error_msg = "Parser stopped before end, but matched with lookahead."
else " too often! Terminating parser.") error_code = Error.PARSER_STOPPED_EXCEPT_FOR_LOOKAHEAD
error_code = Error.PARSER_STOPPED_BEFORE_END max_parser_dropouts = -1 # no further retries!
else:
error_msg = "Parser stopped before end" \
+ (("! trying to recover"
+ (" but stopping history recording at this point."
if self.history_tracking__ else "..."))
if len(stitches) < self.max_parser_dropouts__
else " too often! Terminating parser.")
error_code = Error.PARSER_STOPPED_BEFORE_END
stitches.append(Node(ZOMBIE_TAG, skip).with_pos(tail_pos(stitches))) stitches.append(Node(ZOMBIE_TAG, skip).with_pos(tail_pos(stitches)))
self.tree__.new_error(stitches[-1], error_msg, error_code) self.tree__.new_error(stitches[-1], error_msg, error_code)
if self.history_tracking__: if self.history_tracking__:
......
...@@ -44,12 +44,14 @@ For JSON see: ...@@ -44,12 +44,14 @@ For JSON see:
# TODO: Test with python 3.5 # TODO: Test with python 3.5
import asyncio import asyncio
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, CancelledError from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, CancelledError, \
BrokenExecutor
import json import json
from multiprocessing import Process, Queue, Value, Array from multiprocessing import Process, Queue, Value, Array
import sys import sys
import time import time
from typing import Callable, Coroutine, Optional, Union, Dict, List, Tuple, Sequence, Set, Any, cast from typing import Callable, Coroutine, Optional, Union, Dict, List, Tuple, Sequence, Set, Any, \
cast
from DHParser.syntaxtree import DHParser_JSONEncoder from DHParser.syntaxtree import DHParser_JSONEncoder
from DHParser.toolkit import get_config_value, re from DHParser.toolkit import get_config_value, re
...@@ -255,8 +257,10 @@ class Server: ...@@ -255,8 +257,10 @@ class Server:
rpc_error = -32602, "Invalid Params: " + str(e) rpc_error = -32602, "Invalid Params: " + str(e)
except NameError as e: except NameError as e:
rpc_error = -32601, "Method not found: " + str(e) rpc_error = -32601, "Method not found: " + str(e)
except BrokenExecutor as e:
rpc_error = -32000, "Broken Executor: " + str(e)
except Exception as e: except Exception as e:
rpc_error = -32000, "Server Error: " + str(e) rpc_error = -32000, "Server Error " + str(type(e)) + ': ' + str(e)
if data.startswith(b'GET'): if data.startswith(b'GET'):
# HTTP request # HTTP request
......
...@@ -123,8 +123,33 @@ def flatten_xml(xml: str) -> str: ...@@ -123,8 +123,33 @@ def flatten_xml(xml: str) -> str:
return re.sub(r'\s+(?=<[\w:])', '', re.sub(r'(?P<closing_tag></:?\w+>)\s+', tag_only, xml)) return re.sub(r'\s+(?=<[\w:])', '', re.sub(r'(?P<closing_tag></:?\w+>)\s+', tag_only, xml))
# criteria for finding nodes:
# - node itself (equality)
# - tag_name
# - one of several tag_names
# - a function Node -> bool
CriteriaType = Union['Node', str, Container[str], Callable]
def create_match_function(criterion: CriteriaType) -> Callable:
"""
Returns a valid match function (Node -> bool) for the given criterion.
Match functions are used to find an select particular nodes from a
tree of nodes.
"""
if isinstance(criterion, Node):
return lambda nd: nd == criterion
elif isinstance(criterion, str):
return lambda nd: nd.tag_name == criterion
elif isinstance(criterion, Container):
return lambda nd: nd.tag_name in criterion
if isinstance(criterion, Callable):
return criterion
assert "Criterion %s of type %s does not represent a legal criteria type"
ChildrenType = Tuple['Node', ...] ChildrenType = Tuple['Node', ...]
NoChildren = cast(ChildrenType, ()) # type: ChildrenType NO_CHILDREN = cast(ChildrenType, ()) # type: ChildrenType
StrictResultType = Union[ChildrenType, StringView, str] StrictResultType = Union[ChildrenType, StringView, str]
ResultType = Union[ChildrenType, 'Node', StringView, str, None] ResultType = Union[ChildrenType, 'Node', StringView, str, None]
...@@ -190,7 +215,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil ...@@ -190,7 +215,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
# The following if-clause is merely an optimization, i.e. a fast-path for leaf-Nodes # The following if-clause is merely an optimization, i.e. a fast-path for leaf-Nodes
if leafhint: if leafhint:
self._result = result # type: StrictResultType # cast(StrictResultType, result) self._result = result # type: StrictResultType # cast(StrictResultType, result)
self.children = NoChildren # type: ChildrenType self.children = NO_CHILDREN # type: ChildrenType
else: else:
self.result = result self.result = result
self.tag_name = tag_name # type: str self.tag_name = tag_name # type: str
...@@ -309,7 +334,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil ...@@ -309,7 +334,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
self.children = result self.children = result
self._result = result or '' self._result = result or ''
else: else:
self.children = NoChildren self.children = NO_CHILDREN
self._result = result # cast(StrictResultType, result) self._result = result # cast(StrictResultType, result)
def _content(self) -> List[str]: def _content(self) -> List[str]:
...@@ -459,7 +484,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil ...@@ -459,7 +484,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
# tree traversal and node selection ### # tree traversal and node selection ###
def __getitem__(self, index_or_tagname: Union[int, str]) -> Union['Node', Iterator['Node']]: def __getitem__(self, key: Union[CriteriaType, int]) -> Union['Node', Iterator['Node']]:
""" """
Returns the child node with the given index if ``index_or_tagname`` is Returns the child node with the given index if ``index_or_tagname`` is
an integer or the first child node with the given tag name. Examples:: an integer or the first child node with the given tag name. Examples::
...@@ -471,57 +496,69 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil ...@@ -471,57 +496,69 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
'(X (c "d"))' '(X (c "d"))'
Args: Args:
index_or_tagname(str): Either an index of a child node or a key(str): A criterion (tag name(s), match function, node) or
tag name. an index of the child that shall be returned.
Returns: Returns:
Node: All nodes which have a given tag name. Node: All nodes which have a given tag name.
Raises:
KeyError: if no matching child was found.
IndexError: if key was an integer index that did not exist
ValueError: if the __getitem__ has been called on a leaf node.
""" """
if self.children: if self.children:
if isinstance(index_or_tagname, int): if isinstance(key, int):
return self.children[index_or_tagname] return self.children[key]
else: else:
mf = create_match_function(cast(CriteriaType, key))
for child in self.children: for child in self.children:
if child.tag_name == index_or_tagname: if mf(child):
return child return child
raise KeyError(index_or_tagname) raise KeyError(str(key))
raise ValueError('Leave nodes have no children that can be indexed!') raise ValueError('Leaf-nodes have no children that can be indexed!')
def __contains__(self, tag_name: str) -> bool: def __contains__(self, what: CriteriaType) -> bool:
""" """
Returns true if a child with the given tag name exists. Returns true if a child with the given tag name exists.
Args: Args:
tag_name (str): tag_name which will be searched among to immediate what: a criterion that describes what (kind of) child node
descendants of this node. shall be looked for among the children of the node. This
can a node, a tag name or collection of tag names or an
arbitrary matching function (Node -> bool).
Returns: Returns:
bool: True, if at least one descendant node with the given tag bool: True, if at least one child which fulfills the criterion
name exists, False otherwise exists
""" """
# assert isinstance(tag_name, str)
if self.children: if self.children:
mf = create_match_function(what)
for child in self.children: for child in self.children:
if child.tag_name == tag_name: if mf(child):
return True return True
return False return False
raise ValueError('Leave node cannot contain other nodes') raise ValueError('Leaf-node cannot contain other nodes')
def index(self, tag_name: str, start: int = 0, stop: int = sys.maxsize) -> int: def index(self, what: CriteriaType, start: int = 0, stop: int = sys.maxsize) -> int:
""" """
Returns the first index of the child with the tag name `what`. If the Returns the first index of the child that fulfills the criteriuon
parameters start and stop are given, the search is restricted to the `what`. If the parameters start and stop are given, the search is
children with indices from the half-open interval [start:end[. restricted to the children with indices from the half-open interval
If no such child exists a ValueError is raised. [start:end[. If no such child exists a ValueError is raised.
:param tag_name: the child's tag name for which the index shall be returned :param what: the criterion by which the child is identified, the index
of which shall be returned.
:param start: the first index to start searching. :param start: the first index to start searching.
:param stop: the last index that shall be searched :param stop: the last index that shall be searched
:return: the index of the first child with the given tag name. :return: the index of the first child with the given tag name.
:raises: ValueError, if no child matching the criterion `what` was found.
""" """
assert 0 <= start <= stop assert 0 <= start < stop
i = start i = start
mf = create_match_function(what)
for child in self.children[start:stop]: for child in self.children[start:stop]:
if child.tag_name == tag_name: if mf(child):
return i return i
i += 1 i += 1
raise ValueError("Node with tag name '%s' not among child-nodes." % tag_name) if i >= stop:
break
raise ValueError("Node identified by '%s' not among child-nodes." % str(what))
def select_if(self, match_function: Callable, include_root: bool = False, reverse: bool = False) \ def select_if(self, match_function: Callable, include_root: bool = False, reverse: bool = False) \
-> Iterator['Node']: -> Iterator['Node']:
...@@ -537,7 +574,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil ...@@ -537,7 +574,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
yield child yield child
yield from child.select_if(match_function, False, reverse) yield from child.select_if(match_function, False, reverse)
def select(self, criterion: Union[str, Container[str], Callable], def select(self, criterion: CriteriaType,
include_root: bool = False, reverse: bool = False) -> Iterator['Node']: include_root: bool = False, reverse: bool = False) -> Iterator['Node']:
""" """
Finds nodes in the tree that fulfill a given criterion. This criterion Finds nodes in the tree that fulfill a given criterion. This criterion
...@@ -551,15 +588,15 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil ...@@ -551,15 +588,15 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
See function `Node.select` for some examples. See function `Node.select` for some examples.
Args: Args:
criterion: A function that takes as Node criterion: A "criterion" for identifying the nodes to be selected.
object as argument and returns True or False This can either be a tag name (string), a collection of
tag names or a match function (Node -> bool)
include_root (bool): If False, only descendant nodes will be include_root (bool): If False, only descendant nodes will be
checked for a match. checked for a match.
reverse (bool): If True, the tree will be walked in reverse reverse (bool): If True, the tree will be walked in reverse
order, i.e. last children first. order, i.e. last children first.
Yields: Yields:
Node: All nodes of the tree for which Node: All nodes of the tree which fulfill the given criterion
``match_function(node)`` returns True
Examples:: Examples::
...@@ -574,24 +611,10 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil ...@@ -574,24 +611,10 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
['(a (b "X") (X (c "d")) (e (X "F")))'] ['(a (b "X") (X (c "d")) (e (X "F")))']
>>> flatten_sxpr(next(tree.select("X", False)).as_sxpr()) >>> flatten_sxpr(next(tree.select("X", False)).as_sxpr())
'(X (c "d"))' '(X (c "d"))'
Args:
tag_names(set): A tag name or set of tag names that is being
searched for
include_root (bool): If False, only descendant nodes will be
checked for a match.
Yields:
Node: All nodes which have a given tag name.
""" """
if isinstance(criterion, str): return self.select_if(create_match_function(criterion), include_root, reverse)
return self.select_if(lambda node: node.tag_name == criterion, include_root, reverse)
elif isinstance(criterion, Container):
return self.select_if(lambda node: node.tag_name in criterion, include_root, reverse)
else:
assert isinstance(criterion, Callable)
return self.select_if(criterion, include_root, reverse)
def pick(self, criterion: Union[str, Container[str], Callable], def pick(self, criterion: CriteriaType,
reverse: bool = False) -> Optional['Node']: reverse: bool = False) -> Optional['Node']:
""" """
Picks the first (or last if run in reverse mode) descendant that fulfills Picks the first (or last if run in reverse mode) descendant that fulfills
...@@ -998,7 +1021,7 @@ class RootNode(Node): ...@@ -998,7 +1021,7 @@ class RootNode(Node):
duplicate.children = copy.deepcopy(self.children) duplicate.children = copy.deepcopy(self.children)
duplicate._result = duplicate.children duplicate._result = duplicate.children
else: else:
duplicate.children = NoChildren duplicate.children = NO_CHILDREN
duplicate._result = self._result duplicate._result = self._result
duplicate._pos = self._pos duplicate._pos = self._pos
if self.has_attr(): if self.has_attr():
......
...@@ -342,7 +342,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve ...@@ -342,7 +342,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve
parser[parser_name].apply(find_lookahead) parser[parser_name].apply(find_lookahead)
return lookahead_found return lookahead_found
def lookahead_artifact(st): def lookahead_artifact(syntax_tree: Node):
""" """
Returns True, if the error merely occurred, because the parser Returns True, if the error merely occurred, because the parser
stopped in front of a sequence that was captured by a lookahead stopped in front of a sequence that was captured by a lookahead
...@@ -350,19 +350,22 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve ...@@ -350,19 +350,22 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve
This is required for testing of parsers that put a lookahead This is required for testing of parsers that put a lookahead
operator at the end. See test_testing.TestLookahead. operator at the end. See test_testing.TestLookahead.
""" """
raw_errors = st.errors_sorted raw_errors = syntax_tree.errors_sorted
is_artifact = ((len(raw_errors) == 2 # case 1: superfluous data for lookahead is_artifact = ((2 <= len(raw_errors) == 3 # case 1: superfluous data for lookahead
and raw_errors[-1].code == Error.PARSER_LOOKAHEAD_MATCH_ONLY and {e.code for e in raw_errors} <=
and raw_errors[-2].code == Error.PARSER_STOPPED_BEFORE_END) {Error.PARSER_LOOKAHEAD_MATCH_ONLY,
# case 2: mandatory lookahead failure at end of text # Error.PARSER_STOPPED_BEFORE_END,
Error.PARSER_STOPPED_EXCEPT_FOR_LOOKAHEAD})
or (len(raw_errors) == 1 or (len(raw_errors) == 1
and raw_errors[-1].code == Error.MANDATORY_CONTINUATION_AT_EOF)) and (raw_errors[-1].code == Error.PARSER_STOPPED_EXCEPT_FOR_LOOKAHEAD
# case 2: mandatory lookahead failure at end of text
or raw_errors[-1].code == Error.MANDATORY_CONTINUATION_AT_EOF)))
if is_artifact: if is_artifact:
# don't remove zombie node with error message at the end # don't remove zombie node with error message at the end
# but change it's tag_name to indicate that it is an artifact! # but change it's tag_name to indicate that it is an artifact!
for parent in st.select_if(lambda node: any(child.tag_name == ZOMBIE_TAG for parent in syntax_tree.select_if(lambda node: any(child.tag_name == ZOMBIE_TAG
for child in node.children), for child in node.children),
include_root=True, reverse=True): include_root=True, reverse=True):
zombie = parent[ZOMBIE_TAG] zombie = parent[ZOMBIE_TAG]
zombie.tag_name = '__TESTING_ARTIFACT__' zombie.tag_name = '__TESTING_ARTIFACT__'
zombie.result = 'Artifact can be ignored. Be aware, though, that also the' \ zombie.result = 'Artifact can be ignored. Be aware, though, that also the' \
...@@ -472,7 +475,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve ...@@ -472,7 +475,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve
cst = RootNode(node).new_error(node, str(upe)) cst = RootNode(node).new_error(node, str(upe))
errata.append('Unknown parser "{}" in fail test "{}"!'.format(parser_name, test_name)) errata.append('Unknown parser "{}" in fail test "{}"!'.format(parser_name, test_name))
tests.setdefault('__err__', {})[test_name] = errata[-1] tests.setdefault('__err__', {})[test_name] = errata[-1]
if not is_error(cst.error_flag) and not lookahead_artifact(cst): if not (is_error(cst.error_flag) and not lookahead_artifact(cst)):
errata.append('Fail test "%s" for parser "%s" yields match instead of ' errata.append('Fail test "%s" for parser "%s" yields match instead of '
'expected failure!' % (test_name, parser_name)) 'expected failure!' % (test_name, parser_name))
tests.setdefault('__err__', {})[test_name] = errata[-1] tests.setdefault('__err__', {})[test_name] = errata[-1]
...@@ -850,6 +853,7 @@ def run_path(path): ...@@ -850,6 +853,7 @@ def run_path(path):
def clean_report(): def clean_report():
"""Deletes any test-report-files in the REPORT sub-directory and removes """Deletes any test-report-files in the REPORT sub-directory and removes
the REPORT sub-directory, if it is empty after deleting the files.""" the REPORT sub-directory, if it is empty after deleting the files."""
# TODO: make this thread safe, if possible!!!!
if os.path.exists('REPORT'): if os.path.exists('REPORT'):
files = os.listdir('REPORT') files = os.listdir('REPORT')
flag = False flag = False
......
#!/usr/bin/python3
"""Lädt das MLW-Installationsskript vonb gitlab herunter und startet es.
Nach dem Starten wird das Installationsskript wieder gelöscht, d.h. um
es wieder zu starten, muss erst dieses Skript wieder ausgeführt werden,
welches das jeweils aktuelle Installationsskript herunterlädt.
"""
import os
import ssl
import sys
import urllib.request
cwd = os.path.abspath(os.getcwd())
if cwd.endswith('MLW-DSL'):
print('Der Pfad "%s" scheint das MLW-Git-Repositorium zu enthalten.' % cwd)
print('Dieses Skript sollte nicht innerhalb des MLW-Git-Repositoriums ausgeführt werden!')
sys.exit(1)
PFAD = "https://gitlab.lrz.de/badw-it/MLW-DSL/raw/master/Installiere-MLW.py"
SKRIPT_NAME = os.path.basename(PFAD)
# Lade Installationsskript herunter
with urllib.request.urlopen(PFAD, context=ssl.create_default_context()) as https:
skript = https.read()
# Speichere Installationsskript im aktuellen Ordner (Desktop)
with open(SKRIPT_NAME, 'wb') as datei:
datei.write(skript)
# Führe Installationsskript aus
os.system('python.exe ' + SKRIPT_NAME)
# Lösche Installationsskript nach der Ausführung, damit nicht versehentlich
# ein veraltetes Installationsskript gestartet wird.
os.remove(SKRIPT_NAME)
This diff is collapsed.
...@@ -41,7 +41,7 @@ class TestDHParserCommandLineTool: ...@@ -41,7 +41,7 @@ class TestDHParserCommandLineTool:
if not os.path.exists('testdata'): if not os.path.exists('testdata'):
os.mkdir('testdata') os.mkdir('testdata')
self.nulldevice = " >/dev/null" if platform.system() != "Windows" else " > NUL" self.nulldevice = " >/dev/null" if platform.system() != "Windows" else " > NUL"
self.python = 'python3 ' if os.system('python3 -V' + self.nulldevice) == 0 else 'python ' self.python = 'python3 ' if system('python3 -V' + self.nulldevice) == 0 else 'python '