#๐Ÿ”’ Using imported modules in functions inside of an exec() call

79 messages ยท Page 1 of 1 (latest)

astral shard
#

When I run my code, I get a NameError (screenshot provided) stating that 'Game' is not defined, despite the Game class being imported from gamesys with no issues. The specific issue (if I know what I'm doing) is that 'from gamesys import Game' in 'screenoptions.txt' is not actually working, or in some other way the import does not extend to startnewgame().

Here's the pastebin with all the relevant files: https://paste.pythondiscord.com/E63A

One of the files imports from 'listsys', but that file is no longer in use so the import can be disregarded.

mint turtleBOT
#

@astral shard

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

astral shard
#

Woops, should have marked this 'Game development'

clever parcel
#

Did you write all this by hand?

astral shard
#

Yeah, over the last three days or so

#

Trying my hand at a text-based RPG from first principles

clever parcel
#

I'm impressed with your tenacity, but the architecture is pretty cursed, honestly.

astral shard
#

That's what doing no research into game design does to a mf I suppose lol

clever parcel
#

I'm not trying to offend you or put you down.

#

Again, it's impressive you got this far.

astral shard
#

Oh no I fully understand I'm probably doing most of this incorrectly, I'll be adjusting each class and its implementation as I go through the development process

clever parcel
#

But dynamically loading code from text files and executing them with exec() is, uh, not advisable.

astral shard
#

Yeahhhhhh, I've heard that from literally everyone lol. It's just a temporary fix to the issue of not being able to have buttonsys imported into gamesys and gamesys imported into buttonsys at the same time

clever parcel
#

Oh boy. That's a novel way of dealing with a circular import.

astral shard
#

ACE for the win baybeeee ๐Ÿ”ฅ

#

But yeah, the module import in the exec() won't work for some reason

clever parcel
#

I think you're going to have a much easier time if you solve the circular import in a sane way and avoid the dynamic code execution.

upbeat coyote
#

small note

            for line in lines:
                line = line.strip()

does not change the line that is stored in lines

astral shard
#

I like to put strip() on most of the lines I read from files in case I decide to change my file formatting at a later date, so if you see one that's redundant that's why

astral shard
clever parcel
#

Because you are not modifying the list

#

line.strip() creates a new string and you assign it to line, which is just a name

#

but yeah, that's not what you asked about

astral shard
wary spindle
#

huh

#

redundant code is harmful to readability

astral shard
#

Okay fine I'll remove it

#

Actually no, it removes a newline from the end of each string

#
'n' to start a new game
'r' to resume saved game
's' to open settings
'q' to quit
|```
upbeat coyote
#

What's the reasoning between storing the button code in text files instead of python files?

clever parcel
#

They're doing it to fix a circular import

dire raft
#

Btw, this code:

def savecharacters(charlist):
    with open("C:/Users/rwb38/Drive stuff/CMPS1500/CMPS1500/playground/Game/writables/characters.txt", "w", encoding="utf-8") as file:
        for character in charlist:
            file.write(f"{character.getname()}|{character.getspecies()}|{character.getstaturenoformat()}|{" ".join(character.getstats())}|{character.getspecialfeatures()}\n")

```makes it so that your code can only ever work on your computer with your exact setup, because... well, other people wouldn't have that file(-path)
astral shard
# clever parcel <@223168485419778060>

This, and it also just made sense at the time to made screenvisuals and screenoptions be two parallel files so I could refer to each option using the screen id

astral shard
#

For now I only need it to run on mine

upbeat coyote
astral shard
#

Though I didn't think about that before putting it in pastebin, so I should probably do it soon

astral shard
clever parcel
# astral shard How would you suggest I go about doing that?

My spontaneous thought is to replace the function string in Button with a callable, create the buttons in a separate Python file and maybe register them in a list or something which you can import in screensys, and put the gamesys imports inside the button handler functions, so you only import stuff when you call the functions.

#

Not sure if it's the ideal solution, but it's better than what you have right now.

#

Something like this:

from dataclasses import dataclass
from typing import Callable, Any

@dataclass(frozen=True)
class Button:
    key: str
    label: str
    handler: Callable[[], Any]

# Handlers live here; use lazy imports to avoid cycles.
def start_new_game():
    from gamesys import Game  # lazy import
    g = Game(continuing=False)
    print("New game started:", g)
    return g

def resume_game():
    from gamesys import Game  # lazy import
    g = Game(continuing=True)
    print("Resumed game:", g)
    return g

def show_settings():
    print("Settings go here")
    return {"screen": "settings"}

def quit_app():
    print("Goodbye!")
    raise SystemExit(0)

# Central registry consumed by screensys
BUTTONS = [
    Button("n", "Start new game", start_new_game),
    Button("r", "Resume game",    resume_game),
    Button("s", "Settings",       show_settings),
    Button("q", "Quit",           quit_app),
]
wary spindle
#
def getvisuals(self, id):
    with open("C://Users//rwb38//Drive stuff//CMPS1500//CMPS1500//playground//Game//writables//screenvisuals.txt", "r", encoding="utf-8") as file:
        lines = file.read()
        lines = lines.split("|")
        for line in lines:
            line = line.strip()
        return lines[id]
astral shard
upbeat coyote
#

This also has a secondary problem that shows up multiple times in your code.
Unnecesary loop where you already know you only need the object at [id]

astral shard
upbeat coyote
clever parcel
# astral shard See I'm still in CMPS 1010 so I don't know how to use Callables, registries, or ...

You can disregard the term "registry", it was just a word I came up with to describe having all the buttons in a list that you can import elsewhere. A callable is just a function as a value. You see that I pass just the name of the function to Button, that stores a reference to the function, so you could call it with for example:

new_game_button = Button("n", "Start new game", start_new_game)
game = new_game_button.handler()
upbeat coyote
#

!e

a = ["a ", " b"]
for b in a:
  b = b.strip()
print(a)```
mint turtleBOT
upbeat coyote
#

The original strings in the list still have the extra whitespace

astral shard
#

I see

#

My bad then

clever parcel
# astral shard My bad then

when you do for elem in lst: elem will initially refer to the current element in the list, but if you assign a new value to the name elem, like elem = "str", that overwrites which value the name "elem" refers to.

#

It won't modify the value from the list.

astral shard
#

I'll update that then

upbeat coyote
#

Since you know the id, a loop is not needed here, you can directly return lines[id].strip()

#

and avoid doing the extra work for the strings you wont need anyway

astral shard
upbeat coyote
clever parcel
#

And to put the gamesys imports inside the functions

#

The former point ensures you don't need to do ugly and unsafe exec() calls, and the latter deals with the circular import problem.

astral shard
#

Thanks for the tip! The biggest help is definitely knowing you can import modules inside of functions, I just wasn't aware that worked

#

This also fixes the other issue I was trying to avoid with the janky exec(), which was being able to have the same character perform different actions on different screens

#

Since I don't know how to read inputs without using the basic input() function, I'm gonna have to do a bit more work than you did to get things going, but this is a big help

clever parcel
#

Yeah, that example was just to illustrate the principle

astral shard
clever parcel
#

I tried to focus on the thing you were asking for help with, but there's of course a lot of other things you could improve, like other people have pointed out. If you want more such feedback, let us know.

astral shard
#

I certainly will! I'll leave this post open until it closes automatically in case anyone else in the server has feedback for me

astral shard
#

Welp, still encountering issues

#

But they require a separate ticket I fear

#

!close

mint turtleBOT
#
Python help channel closed with !close

This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.