Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion python/code/wypp/ansi.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def color(s, color):
return color + s + RESET

def green(s):
return color(s, GREEN)
return color(s, GREEN + BOLD)

def red(s):
return color(s, RED + BOLD)
Expand Down
17 changes: 3 additions & 14 deletions python/code/wypp/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,6 @@
from constants import *
from myLogging import *
from exceptionHandler import handleCurrentException
import i18n

def prepareInteractive(reset=True):
print()
if reset:
if os.name == 'nt':
# clear the terminal
os.system('cls')
else:
# On linux & mac use ANSI Sequence for this
print('\033[2J\033[H')


HISTORY_SIZE = 1000

Expand Down Expand Up @@ -48,11 +36,12 @@ def enterInteractive(userDefs: dict, checkTypes: bool, loadingFailed: bool):
historyFile = getHistoryFilePath()
try:
import readline
except:
readline = None
if readline:
readline.parse_and_bind('tab: complete')
if historyFile and os.path.exists(historyFile):
readline.read_history_file(historyFile)
except:
pass
try:
consoleClass(locals=userDefs).interact(banner="", exitmsg='')
finally:
Expand Down
14 changes: 2 additions & 12 deletions python/code/wypp/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@ def init(enableTypeChecking=True):
global _typeCheckingEnabled
_typeCheckingEnabled = enableTypeChecking

def _collectDataClassAttributes(cls):
result = dict()
for c in cls.mro():
if hasattr(c, '__kind') and c.__kind == 'record' and hasattr(c, '__annotations__'):
result = c.__annotations__ | result
return result

def _checkRecordAttr(cls: typing.Any,
ns: myTypeguard.Namespaces,
name: str,
Expand All @@ -48,11 +41,8 @@ def _patchDataClass(cls, mutable: bool, ns: myTypeguard.Namespaces):
fieldNames = [f.name for f in dataclasses.fields(cls)]
setattr(cls, EQ_ATTRS_ATTR, fieldNames)

if hasattr(cls, '__annotations__'):
# add annotions for type checked constructor.
cls.__kind = 'record'
cls.__init__.__annotations__ = _collectDataClassAttributes(cls)
cls.__init__ = typecheck.wrapTypecheckRecordConstructor(cls, ns)
cls.__kind = 'record'
cls.__init__ = typecheck.wrapTypecheckRecordConstructor(cls, ns)

if mutable:
# prevent new fields being added
Expand Down
10 changes: 5 additions & 5 deletions python/code/wypp/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def pythonVersionOk(v):
import runCode
import exceptionHandler
import cmdlineArgs

import ansi

def printWelcomeString(file, version, doTypecheck):
cwd = os.getcwd() + "/"
Expand All @@ -44,8 +44,10 @@ def printWelcomeString(file, version, doTypecheck):
tycheck = ''
if not doTypecheck:
tycheck = ', no typechecking'
printStderr(i18n.tr('=== WELCOME to ') + '"Write Your Python Program" ' +
'(%sPython %s, %s%s) ===' % (versionStr, pythonVersion, file, tycheck))
msg = i18n.tr('=== WELCOME to ') + '"Write Your Python Program" ' + \
'(%sPython %s, %s%s) ===' % (versionStr, pythonVersion, file, tycheck)
fullMsg = (10 * '\n') + ansi.green(msg) + '\n'
printStderr(fullMsg)

def main(globals, argList=None):
(args, restArgs) = cmdlineArgs.parseCmdlineArgs(argList)
Expand All @@ -70,8 +72,6 @@ def main(globals, argList=None):

isInteractive = args.interactive
version = versionMod.readVersion()
if isInteractive:
interactive.prepareInteractive(reset=not args.noClear)

if fileToRun is None:
return
Expand Down
Empty file.
1 change: 1 addition & 0 deletions python/file-test-data/basics/recursive2_ok.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Kinzig
38 changes: 38 additions & 0 deletions python/file-test-data/basics/recursive2_ok.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from __future__ import annotations
from wypp import *

# Ein Flussabschnitt ist entweder
# - ein Bach mit Namen und Quelle, oder
# - ein Zusammenfluss eines Haupt- und Nebenflussabschnitts an einem bestimmten Ort.

@record
class Creek:
origin: str
name: str

@record
class Confluence:
location: str
mainStem: RiverSection
tributary: RiverSection

type RiverSection = Union[Creek, Confluence]

kinzig1 = Creek('Loßburg', 'Kinzig')
gutach = Creek('Schönwald', 'Gutach')
kinzig2 = Confluence('Hausach', kinzig1, gutach)
schutter1 = Creek('Schweighausen', 'Schutter')
heidengraben = Creek('Lahr', 'Heidengraben')
schutter2 = Confluence('Lahr', schutter1, heidengraben)
kinzig3 = Confluence('Kehl', kinzig2, schutter2)

# Name eines Flussabschnitts bestimmen
# Eingabe: den Flussabschnitt (Typ: RiverSection)
# Ergebnis: der Name (Typ: str)
def riverName(r: RiverSection) -> str:
if isinstance(r, Creek):
return r.name
elif isinstance(r, Confluence):
return riverName(r.mainStem)

print(riverName(kinzig3))
Empty file.
1 change: 1 addition & 0 deletions python/file-test-data/basics/recursive_ok.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Kinzig
37 changes: 37 additions & 0 deletions python/file-test-data/basics/recursive_ok.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from wypp import *

# Ein Flussabschnitt ist entweder
# - ein Bach mit Namen und Quelle, oder
# - ein Zusammenfluss eines Haupt- und Nebenflussabschnitts an einem bestimmten Ort.

@record
class Creek:
origin: str
name: str

@record
class Confluence:
location: str
mainStem: RiverSection
tributary: RiverSection

type RiverSection = Union[Creek, Confluence]

kinzig1 = Creek('Loßburg', 'Kinzig')
gutach = Creek('Schönwald', 'Gutach')
kinzig2 = Confluence('Hausach', kinzig1, gutach)
schutter1 = Creek('Schweighausen', 'Schutter')
heidengraben = Creek('Lahr', 'Heidengraben')
schutter2 = Confluence('Lahr', schutter1, heidengraben)
kinzig3 = Confluence('Kehl', kinzig2, schutter2)

# Name eines Flussabschnitts bestimmen
# Eingabe: den Flussabschnitt (Typ: RiverSection)
# Ergebnis: der Name (Typ: str)
def riverName(r: RiverSection) -> str:
if isinstance(r, Creek):
return r.name
elif isinstance(r, Confluence):
return riverName(r.mainStem)

print(riverName(kinzig3))
6 changes: 6 additions & 0 deletions python/file-test-data/basics/recursive_old_fail.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Traceback (most recent call last):
File "file-test-data/basics/recursive_old_fail.py", line 13, in <module>
class Confluence:
File "file-test-data/basics/recursive_old_fail.py", line 15, in Confluence
mainStem: RiverSection
NameError: name 'RiverSection' is not defined
Empty file.
37 changes: 37 additions & 0 deletions python/file-test-data/basics/recursive_old_fail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from wypp import *

# Ein Flussabschnitt ist entweder
# - ein Bach mit Namen und Quelle, oder
# - ein Zusammenfluss eines Haupt- und Nebenflussabschnitts an einem bestimmten Ort.

@record
class Creek:
origin: str
name: str

@record
class Confluence:
location: str
mainStem: RiverSection
tributary: RiverSection

type RiverSection = Union[Creek, Confluence]

kinzig1 = Creek('Loßburg', 'Kinzig')
gutach = Creek('Schönwald', 'Gutach')
kinzig2 = Confluence('Hausach', kinzig1, gutach)
schutter1 = Creek('Schweighausen', 'Schutter')
heidengraben = Creek('Lahr', 'Heidengraben')
schutter2 = Confluence('Lahr', schutter1, heidengraben)
kinzig3 = Confluence('Kehl', kinzig2, schutter2)

# Name eines Flussabschnitts bestimmen
# Eingabe: den Flussabschnitt (Typ: RiverSection)
# Ergebnis: der Name (Typ: str)
def riverName(r: RiverSection) -> str:
if isinstance(r, Creek):
return r.name
elif isinstance(r, Confluence):
return riverName(r.mainStem)

print(riverName(kinzig3))
36 changes: 29 additions & 7 deletions python/fileTests.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
from pathlib import Path
from fileTestsLib import *
import sys

def pythonMinVersion(major: int, minor: int) -> bool:
return sys.version_info >= (major, minor)

def pythonMaxVersion(major: int, minor: int) -> bool:
return sys.version_info <= (major, minor)

directories = [Path("file-test-data/basics"),
Path("file-test-data/extras")]

special = {
'file-test-data/basics/recursive_ok.py': pythonMinVersion(3, 14),
'file-test-data/basics/recursive_old_fail.py': pythonMaxVersion(3, 13)
}

#directories = [Path("file-test-data/basics")]
#directories = [Path("file-test-data/extras")]

for d in directories:
for file in d.iterdir():
if file.is_file():
name = file.as_posix()
if name.endswith('.py'):
check(name)
def main():
for d in directories:
for file in d.iterdir():
if file.is_file():
name = file.as_posix()
if name.endswith('.py'):
if name in special:
if special[name]:
check(name)
else:
check(name)

globalCtx.results.finish()

globalCtx.results.finish()
try:
main()
except KeyboardInterrupt:
pass
20 changes: 18 additions & 2 deletions python/fileTestsLib.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import shutil
import json
import re
import fnmatch

GLOBAL_CHECK_OUTPUTS = True

Expand All @@ -24,6 +25,7 @@ class TestOpts:
keepGoing: bool
record: Optional[str]
lang: Optional[str]
patterns: list[str]

def parseArgs() -> TestOpts:
parser = argparse.ArgumentParser(
Expand All @@ -41,7 +43,9 @@ def parseArgs() -> TestOpts:
type=str, help='Record the expected output for the given file.')
parser.add_argument('--lang', dest='lang',
type=str, help='Display error messages in this language (either en or de, only for recording).')

parser.add_argument('patterns', metavar='PATTERN',
help='Glob patterns, a test is executed only if it matches at least one pattern',
nargs='*')
# Parse the arguments
args = parser.parse_args()

Expand All @@ -53,7 +57,8 @@ def parseArgs() -> TestOpts:
only=args.only,
keepGoing=args.keepGoing,
record=args.record,
lang=args.lang
lang=args.lang,
patterns=args.patterns,
)

defaultLang = 'de'
Expand Down Expand Up @@ -82,6 +87,8 @@ def finish(self):
print(f"Passed: {len(self.passed)}")
print(f"Skipped: {len(self.skipped)}")
print(f"Failed: {len(self.failed)}")
print()
print('Python version: ' + sys.version)
if self.failed:
print()
print("Failed tests:")
Expand Down Expand Up @@ -136,6 +143,9 @@ def shouldSkip(testFile: str, ctx: TestContext, minVersion: Optional[tuple[int,
"""
global _started
opts = ctx.opts
if opts.patterns:
if not matchesAnyPattern(testFile, opts.patterns):
return True
if opts.startAt:
if _started:
return False
Expand Down Expand Up @@ -368,6 +378,12 @@ def checkNoConfig(testFile: str,
if not ctx.opts.keepGoing:
ctx.results.finish()

def matchesAnyPattern(testFile: str, patterns: list[str]) -> bool:
for p in patterns:
if fnmatch.fnmatch(testFile, p) or p in testFile:
return True
return False

def check(testFile: str,
exitCode: int = 1,
minVersion: Optional[tuple[int, int]] = None,
Expand Down
Loading