def easy_args_for_loop():
    from joecceasy import Easy
    Easy.Init(shouldAutoLoadCode=False)

    ## Easy.ArgsE and Easy.Args give us easy access to our programs arguments,
    ## while conveniently not including zero sys.argv[0]
    ## (argument zero) this way we can easily process
    ## just the arguments given to this script.

    for i, arg in Easy.ArgsE:
        print(f"arg at index {i}  (at sys.argv index {i+1}) : {arg}")

    ## Same as the function call, Easy.EnumArgs()
    #for i, arg in Easy.EnumArgs():
    #    print( f"arg at index {i}: in sys index {i+1} : {arg}" )

    ## or, if we didn't want to bother enumerating them:
    #for arg in Easy.Args:
    #    print( f"Easy.Args contains: {arg}" )

    if Easy.ArgsCount < 1:
        print(
            Easy.TrimAndTab(r"""
            ###
            No arguments given.
            Please provide at least one argument.
            For example:
            By dragging and dropping a file onto this script.
        """))
Exemplo n.º 2
0
    def modifyClipboard(self):
        if self.isInProgress == True:
            return self
        self.isInProgress = True
        anyFailed = False
        dictOfRenamed = {}
        btn = self.widgets['button']
        btnWasEnabled = btn.isEnabled()
        btn.setEnabled(False)
        oldLabel = btn.text()
        btn.setText("modifying text in clipboard...")
        self.qapp.processEvents()

        text = self.clipboard.text()  #'plain' )
        #text = ''.join( list(text) )
        textOrig = text

        prefix = self.widgets['prefixLineEdit'].text()
        suffix = self.widgets['suffixLineEdit'].text()
        search = self.widgets['searchLineEdit'].text()
        replace = self.widgets['replaceLineEdit'].text()

        bPrefix = len(prefix)
        bSuffix = len(suffix)
        bSearch = len(search)

        atLoc = self.widgets['whereButtonGroup'].checkedId()

        if len(search):
            if atLoc == ANYWHERE:
                text = text.replace(search, replace)
            if atLoc == START:
                text = Easy.StrReplaceStart(text, search, replace)
            if atLoc == END:
                text = Easy.StrReplaceEnd(text, search, replace)

        if bPrefix and bSuffix:
            text = prefix + text + suffix
        elif bPrefix:
            text = prefix + text
        elif bSuffix:
            text = text + suffix

        self.clipboard.setText(text)
        self.showResultInOutput(text)

        btn.setText(oldLabel)
        btn.setEnabled(btnWasEnabled)
        self.isInProgress = False
Exemplo n.º 3
0
def ytdlIter(urls,
             resultsList,
             location=None,
             checkFunc=None,
             maxEntries=-1,
             forceList=False):
    if isinstance(urls, str):
        urls = [urls]
    if location is None:
        location = Easy.Cwd
    if checkFunc is None:
        checker = Checker(location)
        checkFunc = checker.check
    for url in urls:
        #print( 'iterating...' )
        if (forceList == True or (("?list=" in url or "&list=" in url)
                                  and not ('?v=' in url or '&v=' in url))
                or '/c/' in url or url.endswith('/videos')
                or url.endswith('/featured')
                or ('/user/' in url and (not '&v=' in url or '?v=' in url))):
            try:
                #print( f'treating as playlist: {url}' )
                ids = ytGetPlaylistIds(url)
                #print( f'Adding to urls from id list found in playlist... ')
                ##, id list was: {ids}' )
                for id in ids:
                    urls.append('https://www.youtube.com/watch?v=' + id)
            except KeyboardInterrupt as err:
                raise err
            except:
                Easy.PrintTraceback()
        else:
            try:
                _ytDownload(url,
                            resultsList=resultsList,
                            location=location,
                            checkFunc=checkFunc)
                #print( 'try end' )
            except KeyboardInterrupt as err:
                raise err
            except:
                Easy.PrintTraceback()
        #print( 'yielding' )
        yield resultsList
Exemplo n.º 4
0
def main():
    tt = Easy.TrimAndTab
    location = tt(r"""
        #
        D:\music-on-wilbert\tmp\testEasyYt
    """)
    location = Easy.AbsPath(location)
    urls = [
        ## a standalone short video
        'https://www.youtube.com/watch?v=UO_QuXr521I',
        ## aaa-supershort playlist
        'https://www.youtube.com/playlist?list=PLiCtOh7e1jKo5v8aIhKMxH56baLU5xlHJ',
        ## should always fail because it's in the above playlist
        'https://www.youtube.com/watch?v=9RTaIpVuTqE',
    ]
    Easy.Ytdl(
        urls,
        location,
    )
Exemplo n.º 5
0
    def searchClipboard(self):
        text = self.clipboard.text()
        if not len(text):
            return
        search = self.widgets['searchLineEdit'].text()
        replace = self.widgets['replaceLineEdit'].text()
        bSearch = len(search)
        atLoc = self.widgets['whereButtonGroup'].checkedId()

        if len(search):
            if atLoc == ANYWHERE:
                text = text.replace(search, replace)
            if atLoc == START:
                text = Easy.StrReplaceStart(text, search, replace)
            if atLoc == END:
                text = Easy.StrReplaceEnd(text, search, replace)

        self.clipboard.setText(text)
        self.showResultInOutput(text)
Exemplo n.º 6
0
 def __init__(self):
     self.check = False
     self.url = None
     self.attempStartTimeStr = Easy.NowLocalStr()
     self.workDir = None
     self.dlFname = None
     self.fname = None
     self.txtFile = None
     self.resultEx = None  ## result obj from extracted info stage
     self.resultDl = None  ## result obj from dl stage
     self.resultEntries = []
def test_Utils_funcs_Fstring02():
    from joecceasy import Easy

    exampleToFormat = "example number is: {number}"
    ## note that at this point the substitution hasn't happened yet

    ## now number is defined, late
    number = 5
    substitutions = {'number': number}

    ## print while substituting in local variables and capture result...
    r1 = Easy.PrintWithFormat(exampleToFormat, **locals())
    ## or, substituting named filed with given value..
    r2 = Easy.PrintWithFormat(exampleToFormat, number=number)
    ## alternate methods of doing the same, with optional end
    r3 = Easy.PrintWithFormatV(exampleToFormat, kwargs=locals(), end='\n\n\n')

    # we don't have to print the results
    r4 = Easy.Format(exampleToFormat, number=5)
    r5 = Easy.FormatV(exampleToFormat, kwargs=substitutions)
    # or without, keyword arguments, first locals then globals, use None to skip
    r6 = Easy.FormatV(exampleToFormat, None, substitutions)

    resultPyFmt = exampleToFormat.format(**locals())
    ## nothing wrong with this other than too much syntax
    #print ( f'pure python example: {resultPyFmt}'  )
    assert resultPyFmt == r1
    assert resultPyFmt == r2
    assert resultPyFmt == r3
    assert resultPyFmt == r4
    assert resultPyFmt == r5
    assert resultPyFmt == r6
Exemplo n.º 8
0
 def check(self, id):
     ## clean up id in case it has other data in it
     if '&' in id:
         ampAt = id.index('&')
         id = id[:ampAt]
     location = self.location
     listed = Easy.Ls(location)
     #print( f'listed: {listed}')
     isOk = True
     for l in listed:
         #print( f'comapre  id: {id}   l: {l}' )
         if id + '.' in l or l.endswith(
                 id) or f'?id={id}' in l or f'&id={id}' in l:
             isOk = False
     return isOk
def test_Utils_Funcs_Fstring_01():
    from joecceasy import Easy

    exampleToFormat = "example number is: {number}"
    ## note that at this point the substitution hasn't happened yet

    ## now number is defined, late
    number = 2

    ## now we want to use our number, late
    result = eval(Easy.Fstring(exampleToFormat))

    ## at this point the substitution should be done

    ## here's the normal python way
    resultPyFmt = exampleToFormat.format(**locals())

    #print ( f'pure python example: {resultPyFmt}'  )
    assert resultPyFmt == result
from joecceasy import Easy
QtWidgets = Easy.Mods['PySide2.QtWidgets']

instructions = "Instructions: " \
"This is some instructional text. It can be multiple lines if you like. " \
"Click Go to get a message!\n"


def additionalWidgets(ui):
    qw = Easy.Mods.PySide2.QtWidgets
    ui.mainLayout.addRow(
        qw.QLabel('This is an additional row!'),
        qw.QPushButton('click me'),
    )


qtui = Easy.Qtui(
    title="JoecceasyQtuiExample",
    instructionsText=instructions,
    showInput=False,
    callbacks={
        'initAdditionalWidgetsIntoDefault': additionalWidgets,
        'initWidgetsPre': additionalWidgets,
        'initWidgetsPost': additionalWidgets
    },
)
exitCode = qtui.execQapp()  ## qtui.qapp.exec_() would do the same
Easy.Exit(exitCode)  ## just uses sys.exit
Exemplo n.º 11
0
    def convertFilesOnClipboard(self):
        if self.isConversionInProgress == True:
            return self
        self.isConversionInProgress = True
        btn = self.widgets['button']
        btnWasEnabled = btn.isEnabled()
        btn.setEnabled(False)
        oldLabel = btn.text()
        btn.setText("converting...")
        self.qapp.processEvents()

        clip = self.Qtg.QGuiApplication.clipboard()
        mimeData = clip.mimeData()
        urls = mimeData.urls()
        paths = [path.toLocalFile()[0:] for path in urls]
        #self.print( paths )

        import sys
        import subprocess
        import time
        import traceback
        args = paths
        argsCount = len(args)
        ffmpeg = self.widgets['ffmpegLineEdit'].text()
        ##"ffmpeg"  ## works on most linux systems if ffmpeg is installed
        self.print("Attempting to convert to mp3 files...")
        try:
            for i, arg in enumerate(args):
                self.qapp.processEvents()
                inFile = arg
                outFile = self.replaceOrAppendExtension(inFile)
                ffmpeg = "ffmpeg"

                #infoCmd = '"ffmpeg"' + " -i " + inFile
                #subprocess.call( )
                spc = ' '
                isCopyCodec = self.widgets['codecCopyCheckbox'].isChecked()
                #False

                fOptsList = []

                optBitrate = self.widgets['bitrateLineEdit'].text()
                optBitrateStr = str(int(optBitrate))
                optBitrateStr += 'k'

                fOptsFlagsList = ['-vn', '-sn']

                fOptsCopyList = ['-c:a', 'copy']

                fOptsAudioBitrateList = \
                    ['-b:a',
                      optBitrateStr,
                    ]

                fOptsList.extend(fOptsFlagsList)

                if isCopyCodec:
                    fOptsList.extend(fOptsCopyList)
                else:
                    fOptsList.extend(fOptsAudioBitrateList)
                """
                cmd = ( '"' + ffmpeg + '"'
                    + " -n -i " 
                    + '"' + inFile + '"'
                    + fOptsStr
                    + '"' + outFile + '"'
                )
                """
                cmdList = [ffmpeg, '-n', '-i', inFile]
                cmdList.extend(fOptsList)
                cmdList.append(outFile)
                cmd = cmdList

                #cmd = f"echo {inFile}"

                self.print(f"cmd is: {cmd}")
                ## -vn  discards video
                ## -sn discards subtitles
                ## -loglevel panic
                msg = f"Converting file {i+1} of {argsCount}:\n" \
                        + "" + inFile + "\n" \
                        + "To:\n" \
                        + "" + outFile
                self.print(msg)

                try:
                    tub = Easy.Tubprocess(cmd)
                    for v in tub:
                        self.print(v.out, v.err, end='')

                    retVal = tub.wait()  ## only needed for its cleanup sidefx
                    if retVal == 0:
                        self.print("\n...done")
                    else:
                        self.print(
                            f"...ffmpeg exited with error code: {retVal}")
                except:
                    self.print("An error occured.")
                    self.print(Easy.Traceback())

        except:
            self.print(Easy.Traceback())

        self.print(
            Easy.TrimAndTab(r"""
            ##
            ...finished the attempt to process all files.
            Any failures/errors should be noted above.
            ===============================
        """))

        btn.setText(oldLabel)
        btn.setEnabled(btnWasEnabled)
        self.isConversionInProgress = False
Exemplo n.º 12
0
from joecceasy import Easy

## make a qtui that uses a timer based callback
## to run code on update
## using function as lambda inline
qtui = Easy.Qtui( title="JoecceasyQtuiExample",
    updateInterval=1000,
    callbacks={
        'update' :  lambda self:  self.print(
            f"Update at time: {Easy.Mods.time.time()}"
        ),
    },
    showInput=False,
    autoExpandOutput=True, 
)
exitCode = qtui.execQapp()
Easy.Exit( exitCode )
from joecceasy import Easy

cmd = ["python.exe", "-c", "import time; print(1); time.sleep(1); print(2)"]
tub = Easy.Tubprocess(cmd)
for i in tub:
    print(i.out, end='')
Exemplo n.º 14
0
from random import randint
import time

from joecceasy import Easy 

#from asciimatics.screen import Screen
#Screen = Easy.Ascui.AsciimaticsMod.screen.Screen

#Easy.Mods.sys.exit(  )

## Multiples can be used in sequence if you want multiple steps...

## First one, minor customization, no custom class
## note that since this functions as first screen only,
## we show "Next" instead of "Quit"
Easy.Ascui(title='Ascui Examples Step 1 of 2', quitLabel="Next", quitAskMsg='').exec_()

## Second one, via, customized subclass
class ExampleAscui( Easy.Ascui ):
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
    def initWidgets(self):
        self.frame.createWidget("Text", "MyText", "My Text" )
        self.frame.createWidget("Divider", "Divider01", None, height=3 )
        
        self.frame.createWidget("Button", "Do Nothing", None, layoutCol=0, inFooter=True )
        self.frame.createWidget("Button", "Show Anim Msg", None,
            layoutCol=1, inFooter=True,
            callback=lambda:
              Easy.Ascui.FullscreenMsg(
                msg="Button was pressed!",
Exemplo n.º 15
0
from joecceasy import Easy

intro = r"""
Easy.Input Example
======================

This script has inputs with timeouts!

Easy.Input is clumsy, only use it for simplest cases.

Behaviour of further calls to Easy.Input, after one of them times out, is awful in terms of user experience. Perhaps use Ascui instead? Or something like whip tail etc.


"""

print(intro, end="")

greeting = Easy.Input("\n\n" + "Please enter a greeting (5 second timeout):",
                      5, "Hello")
print(f'greeting is: {repr(greeting)}')

message = Easy.Input("\n\n" + "Please enter a message (6 sec timeout):",
                     timeout=6,
                     default="It's a wonderful world.  :)  ")
print(f'message is: {repr(message)}')
from random import randint
import time

from joecceasy import Easy

#from asciimatics.screen import Screen
#Screen = Easy.Ascui.AsciimaticsMod.screen.Screen

#Easy.Mods.sys.exit(  )

## Multiples can be used in sequence if you want multiple steps...

## First one, minor customization, no custom class
## note that since this functions as first screen only,
## we show "Next" instead of "Quit"
Easy.Ascui(title='Ascui Examples Step 1 of 2', quitLabel="Next").exec_()


## Second one, via, customized subclass
class ExampleAscui(Easy.Ascui):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def initWidgets(self):
        self.frame.createWidget("Text", "MyText", "My Text")
        # self.frame.createWidget("Divider", "Divider01", None, height=3 )

        self.frame.createWidget("Button",
                                "Do Nothing",
                                None,
                                layoutCol=0,
from joecceasy import Easy

cmd = [
    "python.exe", "-c",
    "import time; print(1); time.sleep(1); print(2); time.sleep(1); print(3)"
]
tub = Easy.Tubprocess(cmd, autoPrint=True)
tub.wait()
from joecceasy import Easy

instructions = "Instructions: " \
"This is some instructional text. It can be multiple lines if you like. " \
"Click Go to get a message!\n"


def onGoButtonClicked():
    global qtui
    qtui.print( "Go button was clicked at epoch time: " \
        f"{Easy.Mods.time.time()}" )


qtui = Easy.Qtui(
    title="JoecceasyQtuiExample",
    instructionsText=instructions,
    showInput=False,
    callbacks={'onGoButtonClicked': onGoButtonClicked},
)
exitCode = qtui.execQapp()  ## qtui.qapp.exec_() would do the same
Easy.Exit(exitCode)  ## just uses sys.exit
Exemplo n.º 19
0
from joecceasy import Easy

if True:
    trimmed = Easy.TrimAndTab(r"""
        ## first non whitespace must be "#", this line gets removed and others unindented to match
        Here's some trimmed lines,
        trimmed lines are really useful.
        The literal code has nice indentation.
        Code is easier to copy/paste and it's easier to write big strings
        inside in functions/classes.
        They can end (from a practical perspective)
        with windows backslashes too!
        eg.
        C:\Windows\
    """)

print(trimmed)
from joecceasy import Easy
from joecceasy import Easy

instructions = Easy.TrimAndTab(r"""
    ##
    Instructions:
    This is some instructional text.
    It can be multiple lines if you like.
""")

## Create and run a Qtui app with more customization
qtui = Easy.Qtui(
    title="Joecceasy Qtui  Example - Customization",
    tabTitle='DefaultTab',
    showInput=False,
    showInstructions=True,
    instructionsText=instructions,
)
exitCode = qtui.execQapp()
Easy.Exit(exitCode)
from joecceasy import Easy
spc = ' '

echoCmdAndArgs = [
    r'''echo''', r'''test!''', r'''another>test''', r'''test three''',
    r'''c:\some dir\file.txt'''
]
echoCmdAndArgs = Easy.EscArgsForWin(echoCmdAndArgs)
print("esc: ", echoCmdAndArgs)
Easy.CallInteractive(echoCmdAndArgs, loud=True)

pyCmdAndArgs = [
    r'''python''', r'''-c''',
    r'''import sys; print( sys.argv[0] ); print( len(sys.argv) ); print('hello you'); print( "hello again")'''
]
pyCmdAndArgs = Easy.EscArgsForCmdExe(pyCmdAndArgs)
print("esc: ", pyCmdAndArgs)
Easy.CallInteractive(pyCmdAndArgs, loud=True)
##
## most useful example from here:  https://www.attrs.org/en/stable/examples.html

from joecceasy import Easy ; #Easy.Init()




@Easy.DataClass
class MyDataClass:
    simple: bool = True
    dataclassesAreTheOnlyOption: bool = False
    thisCouldBeAnything: object = None

myDataInstance = MyDataClass( )
Easy.Ic( myDataInstance )




Point2d = Easy.NamedTuple(
    'Point2d',
        ['x',
         'y'])

point2d = Point2d( 5, -2 )
Easy.Ic( point2d )



Exemplo n.º 23
0
#!/usr/bin/python3
from joecceasy import Easy

pkg = "isodate"  ## note that this testing script
## assumes pkg name is same as mod name,
## like "isodate" example.
## The test will fail otherwise,
## but Easy.PipInstall feature can be used without
## matching names as long as care is taken to use the
## correct different pkg and mod names

try:
    pkgMod = Easy.Mods[pkg]
except:
    print(f""" {pkg} package not found, attempting pip install...""")
    Easy.PipInstall(pkg)
    print(f""" {pkg} package installed """)
    pkgMod = Easy.Mods[pkg]

print(f"dir function used on pkg {pkg} shows:")
print(dir(pkgMod))

Easy.Input('press enter to exit or wait 5 sec for timeout', 5, warn=0)
Exemplo n.º 24
0
def main():
    
    paths = ['..','.']
    absOfEntries = [ i.abs for i in Easy.WalkAnIter(paths)  ]
    for i in absOfEntries:
        print( i )
Exemplo n.º 25
0
    def replaceOrAppendExtension(self, inFile):
        ## default
        outFile = inFile + ".mp3"

        if False:
            "pass"
        elif inFile.endswith('.flac'):
            outFile = Easy.ReplaceEnd(inFile, ".flac", ".mp3")
        elif inFile.endswith('.mp4'):
            outFile = Easy.ReplaceEnd(inFile, ".mp4", ".mp3")
        elif inFile.endswith('.m4a'):
            outFile = Easy.ReplaceEnd(inFile, ".m4a", ".mp3")
        elif inFile.endswith('.mkv'):
            outFile = Easy.ReplaceEnd(inFile, ".mkv", ".mp3")
        elif inFile.endswith('.ogg'):
            outFile = Easy.ReplaceEnd(inFile, ".ogg", ".mp3")
        elif inFile.endswith('.opus'):
            outFile = Easy.ReplaceEnd(inFile, ".opus", ".mp3")
        elif inFile.endswith('.webm'):
            outFile = Easy.ReplaceEnd(inFile, ".webm", ".mp3")
        elif inFile.endswith('.avi'):
            outFile = Easy.ReplaceEnd(inFile, ".avi", ".mp3")
        elif inFile.endswith('.mpeg'):
            outFile = Easy.ReplaceEnd(inFile, ".mpeg", ".mp3")
        elif inFile.endswith('.mpg'):
            outFile = Easy.ReplaceEnd(inFile, ".mpg", ".mp3")
        elif inFile.endswith('.aac'):
            outFile = Easy.ReplaceEnd(inFile, ".aac", ".mp3")
        elif inFile.endswith('.wav'):
            outFile = Easy.ReplaceEnd(inFile, ".wav", ".mp3")
        else:
            "pass"  ## Default case already handled

        return outFile
Exemplo n.º 26
0
#!/usr/bin/env python3
from joecceasy import Easy
Easy.Init()

## current working dir is auto changed
## when using typical initialization
## of Easy class instance.
## e.g. when accessing Easy.Magic
## or calling Easy.Init()
##
## So here, current working dir is changed,
## but... other dirs are kept track of!  Yey!
print("current dir is:")
print(Easy.Cwd)  ## same as os.getcwd()
print("but also remembers original working dir:")
print(Easy.OrigWorkDir)
print("script dir is available even after changing directory:")
#Easy.Chdir( )
print(Easy.ScriptDir)
print(
    "If script was started when working dir was script's dir, the above 3 will all be the same."
)
Exemplo n.º 27
0
from joecceasy import Easy
from joecceasy import Easy

instructions = Easy.TrimAndTab(r"""
    ##
    Instructions:
    This is some instructional text.
    It can be multiple lines if you like.
""")


def additionalWidgetsInMain(qtui):
    qw = qtui.QtWidgets
    qtui.mainLayout.addRow(
        qw.QLabel('This is an added row!'),
        qw.QPushButton('click me'),
    )


## Create and run a Qtui app with more customization
qtui = Easy.Qtui(
    title="Joecceasy Qtui  Example - Customization",
    tabTitle='DefaultTab',
    showInput=False,
    useOutput=False,
    callbacks={
        'initAdditionalWidgetsIntoDefault': additionalWidgetsInMain,
    },
)
exitCode = qtui.execQapp()
Easy.Exit(exitCode)
from joecceasy import Easy

#tub = Easy.Tubprocess( ["python.exe", "example__func__PrintLoop__01_a.py"], ) #errToOut=True )

for i in Easy.Tubprocess([
        "python.exe", "-c",
        "import time; print(1); time.sleep(1); print(2); time.sleep(1); print(3)"
]):
    print(i.out, end='')
exit()

for i, (out, err), in enumerate(tub):
    print(out, end='')
    #print( err, end='' )
    if i > 80:
        break

print('break')
print(tub.outStr)
Easy.Mods.time.sleep(1.5)
print("next")
print(tub.next().out)
print("resuming")

for out, err2 in tub:
    print(out, end='')
    print(err2, end='')

print(tub.outStr[-25:])
Exemplo n.º 29
0
from joecceasy import Easy

Easy.Init()


## When making a subclass, the init function
## should pass through
## *args, then optional arguments, then **kwargs
## using a pattern like below, otherwise there may be issues
## trying to pass through optional arguments
class MyQtui(Easy.Qtui):
    def __init__(self,
                 *args,
                 title="Joecceasy Qtui  Example - Subclass",
                 showInput=False,
                 autoExpandOutput=True,
                 updateInterval=3000,
                 **kwargs):
        super().__init__(
            *args,
            title=title,
            showInput=showInput,
            autoExpandOutput=autoExpandOutput,
            updateInterval=updateInterval,
            **kwargs,
        )

    def initAdditionalWidgetsIntoDefault(self):
        qw = Easy.Mods.PySide2.QtWidgets
        msgBtn = self.widgets["msgButton"] = qw.QPushButton('click me')
        self.mainLayout.addRow(msgBtn)
Exemplo n.º 30
0
import os, sys, traceback, subprocess

from joecceasy import Easy

timeout = 5
print(f'About to get input (with timeout)...')
print(f'Note that default input will be received after {timeout} seconds.')
got = Easy.Input(f'Input - hurry, before {timeout} second timeout!:',
                 timeout=timeout,
                 default=None)
print(f'received input was: {got}\n{repr(got)}    {type(got)}')