JSearUK / Playthroughs

The aim is to create the system in such a way that it can be safely dropped into any other project
The Unlicense
0 stars 0 forks source link

AttributeError: 'str' object has no attribute 'isdecimal' #3

Closed KiloOscarSix closed 2 years ago

KiloOscarSix commented 3 years ago
I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/screens.rpy", line 632, in execute
    screen load():
  File "game/screens.rpy", line 632, in execute
    screen load():
  File "game/screens.rpy", line 636, in execute
    use file_slots(_("Load"))
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 291, in execute
    # Some 'ysize's in this screen are set to 'yvalue', if 'gui.text_size' is significantly smaller than the icon height (30px + 1px border). This helps to line up various edges
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 291, in execute
    # Some 'ysize's in this screen are set to 'yvalue', if 'gui.text_size' is significantly smaller than the icon height (30px + 1px border). This helps to line up various edges
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 295, in execute
    # Use the Ren'Py save system
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 299, in execute
    fixed:
  File "game/screens.rpy", line 414, in execute
    screen game_menu(title, scroll=None):
  File "game/screens.rpy", line 414, in execute
    screen game_menu(title, scroll=None):
  File "game/screens.rpy", line 423, in execute
    frame:
  File "game/screens.rpy", line 426, in execute
    hbox:
  File "game/screens.rpy", line 432, in execute
    frame:
  File "game/screens.rpy", line 435, in execute
    if scroll == "viewport":
  File "game/screens.rpy", line 465, in execute
    transclude
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 299, in execute
    fixed:
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 300, in execute
    # Added a button to switch to the Playthroughs system. This doesn't need to reference 'yvalue' because there is no text or edges around that need to be aligned with
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 341, in execute
    style_prefix "page" xalign 0.5 yalign 1.0 spacing gui.page_spacing
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 356, in execute
    global lastpage
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 364, in <module>
    # Preserve the last numeric page viewed
AttributeError: 'str' object has no attribute 'isdecimal'

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "renpy/common/_layout/screen_main_menu.rpym", line 28, in script
    python hide:
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\ast.py", line 914, in execute
    renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\python.py", line 2028, in py_exec_bytecode
    exec bytecode in globals, locals
  File "renpy/common/_layout/screen_main_menu.rpym", line 28, in <module>
    python hide:
  File "renpy/common/_layout/screen_main_menu.rpym", line 35, in _execute_python_hide
    ui.interact()
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\ui.py", line 297, in interact
    rv = renpy.game.interface.interact(roll_forward=roll_forward, **kwargs)
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\core.py", line 2702, in interact
    repeat, rv = self.interact_core(preloads=preloads, trans_pause=trans_pause, **kwargs)
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\core.py", line 3094, in interact_core
    root_widget.visit_all(lambda i : i.per_interact())
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\core.py", line 541, in visit_all
    d.visit_all(callback, seen)
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\core.py", line 541, in visit_all
    d.visit_all(callback, seen)
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\core.py", line 541, in visit_all
    d.visit_all(callback, seen)
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\screen.py", line 430, in visit_all
    callback(self)
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\core.py", line 3094, in <lambda>
    root_widget.visit_all(lambda i : i.per_interact())
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\screen.py", line 440, in per_interact
    self.update()
  File "E:\Projects\On-Going\Ren-Py-Playthroughs-Save-System\renpy\display\screen.py", line 625, in update
    self.screen.function(**self.scope)
  File "game/screens.rpy", line 632, in execute
    screen load():
  File "game/screens.rpy", line 632, in execute
    screen load():
  File "game/screens.rpy", line 636, in execute
    use file_slots(_("Load"))
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 291, in execute
    # Some 'ysize's in this screen are set to 'yvalue', if 'gui.text_size' is significantly smaller than the icon height (30px + 1px border). This helps to line up various edges
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 291, in execute
    # Some 'ysize's in this screen are set to 'yvalue', if 'gui.text_size' is significantly smaller than the icon height (30px + 1px border). This helps to line up various edges
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 295, in execute
    # Use the Ren'Py save system
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 299, in execute
    fixed:
  File "game/screens.rpy", line 414, in execute
    screen game_menu(title, scroll=None):
  File "game/screens.rpy", line 414, in execute
    screen game_menu(title, scroll=None):
  File "game/screens.rpy", line 423, in execute
    frame:
  File "game/screens.rpy", line 426, in execute
    hbox:
  File "game/screens.rpy", line 432, in execute
    frame:
  File "game/screens.rpy", line 435, in execute
    if scroll == "viewport":
  File "game/screens.rpy", line 465, in execute
    transclude
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 299, in execute
    fixed:
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 300, in execute
    # Added a button to switch to the Playthroughs system. This doesn't need to reference 'yvalue' because there is no text or edges around that need to be aligned with
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 341, in execute
    style_prefix "page" xalign 0.5 yalign 1.0 spacing gui.page_spacing
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 356, in execute
    global lastpage
  File "game/JSearUK's Save System/overridescreen-fileslots.rpy", line 364, in <module>
    # Preserve the last numeric page viewed
AttributeError: 'str' object has no attribute 'isdecimal'

Windows-8-6.2.9200
Ren'Py 7.3.5.606
The Question Ren'Py 7 Edition
Fri Aug 06 21:42:30 2021
KiloOscarSix commented 3 years ago

Need to use try/except ValueError: instead of isdecimal()

JSearUK commented 3 years ago

Will look into this. I note that this doesn't happen if I drop it into a 7.4.x build, though this doesn't help much as most novels either were or are still being built with 7.3.5 or below. That .isdecimal() check is supposed to prevent int() throwing an exception if the string is NaN (including an empty string) - is there a better/standard way of dealing with this?

KiloOscarSix commented 3 years ago

The best way with dealing with possible exceptions is using exception handling.

https://www.w3schools.com/python/python_try_except.asp

Looking at your current code I've made some edits but a couple places it might be a bit more fiddling to fix.

JSearUK commented 3 years ago

Okay, so that's useful for testing code, in case something goes wrong; it can gracefully tell you about it and, if handled correctly, potentially recover without crashing.

In this case, though, we know something's going wrong: a python method that should be available on the variable (because it's a string) is not available. We know it's a string, because that is what is returned: https://www.renpy.org/doc/html/screen_actions.html#FileCurrentPage I even tested it by explicitly casting the returned value to a string: currentpage = str(FileCurrentPage()) ...so why is the method not available?

I am not arguing, btw. In any case, I could write a SafeInt() function that does the checking for me (and might do just that). I do want to know why what is there is not working when dropped into a 7.3.5 build, but works just fine in a 7.4.x build, and whether I can do something more elegant than shrug and develop a workaround (which is what I've done so far lol)

KiloOscarSix commented 3 years ago

I've read that it could work if we cast the string to a Unicode object. I'm back home tonight so can do some testing.

On another note, it's better practise to use error handling than if statements. The saying is "if it quacks like a duck, looks like a duck, it is a duck"

This can be better explained when I have a couple examples tonight.

JSearUK commented 3 years ago

image

KiloOscarSix commented 3 years ago

Also don't worry about questioning/arguing. The more we think and discuss problems the better the solution we'll come up with.

JSearUK commented 3 years ago

Good to know :)

While I am here - I have to take a child to an appointment shortly - I should let you know that I am typically available between 15:00-21:00 GMT. I work early in the morning, which is unfortunate because you guys seem to be geared for evening/night hours (actually, I am, too - my work hours suck balls)

KiloOscarSix commented 3 years ago

Example Before:

                        if lastpage == None: lastpage = int(currentpage) if currentpage.isdecimal() else 1
                        # Preserve the last numeric page viewed
                        currentpage = int(currentpage) if currentpage.isdecimal() else lastpage

After:

                        if lastpage is None:
                            try: lastpage = int(currentpage)
                            except ValueError: lastpage = 1
                        # Preserve the last numeric page viewed
                        try: currentpage = int(currentpage)
                        except ValueError: currentpage = lastpage
KiloOscarSix commented 3 years ago

Been fixed with #4

JSearUK commented 2 years ago
JSearUK commented 2 years ago

Yeah, dropping it into 7.3.5 presents this crash. Will have to fix all that for compatibility, later

JSearUK commented 2 years ago

Fixed it.

                # Conditional invalidation based on purpose of input can go here:
                if u"{}".format(currentstring).isdecimal():
                    # If 'targetaction' is "newplaythroughname" or "changeplaythroughname", we need to prevent it from being an integer (because these are Ren'Py save system Page names)
                    # The `.isdecimal()` method is only available on unicode objects. `.isdigit()` is a problem because e.g. exponents, like ², are also considered to be a digit.
                    # Explicitly converting to unicode resolves compat with 7.3.5. However, Py3 uses unicode natively for strings and might get upset about converting to itself
                    # TODO: Test on 7.5
                    # TODO: Test on 8
                    if targetaction in ("newplaythroughname", "changeplaythroughname"):
                        isvalid = False
                        feedback = _("{b}The Playthrough name may not be a whole number{/b}")
                elif invalid:
                    # Since Playthrough names must be unique, it is invalid if it already exists in the list
                    if currentstring in invalid:
                        isvalid = False
                        feedback = _("{b}This Playthrough name already exists{/b}")